/*
 * ************************************************************************************************************
 * Author: Marcin Załęczny
 * Copyright (C)2010 Marcin Załęczny <mzaleczny@hotmail.com>
 * License: The GNU Lesser General Public License, version 3.0 (LGPLv3)
 * Library Name: mzstd
 * Version: 1.07
 * First release date:   2010-11-08
 * Current release date: 2011-03-04
 *
 *
 * This file is part of mzstd library
 * 
 * mzstd library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * mzstd library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * ************************************************************************************************************
 */

#include "mzMap.h"
#include "mzstdGlobals.h"

// ***********************************************************************************************
// FUNKCJE
// ***********************************************************************************************
VOID WINAPI swap(LONG& val1, LONG& val2) {
    LONG tmp = val1;
    val1 = val2;
    val2 = tmp;
}

VOID WINAPI swap(BOOL& val1, BOOL& val2) {
    LONG tmp = val1;
    val1 = val2;
    val2 = tmp;
}

VOID WINAPI swap(CPair* pair1, CPair* pair2) {
    pair1->swap(pair2);
}

VOID WINAPI swap(TPairExtValueType& val1, TPairExtValueType& val2) {
    TPairExtValueType tmp = val1;
    val1 = val2;
    val2 = tmp;
}

VOID WINAPI deleteArrayListOfPairExt(CArrayList *lst) {
    ULONG size = lst->size();
    if (!lst || size < 1) {
        return;
    }

    LONG* items = lst->getItemsBuf();
    CPairExt* p;

    for (ULONG i = 0; i < size; i++) {
        p = (CPairExt*)items[i];
        if (p) {
            delete p;
        }
    }

    lst->clear();
    delete lst;
}
// ***********************************************************************************************
// KONIEC FUNKCJI
// ***********************************************************************************************




// ***********************************************************************************************
// Klasa CPair
// ***********************************************************************************************
VOID CPair::_commonInit() {
    this->setRealClassName(L"CPair");
}

CPair::CPair()
	: first(0), second(0)
{
    this->_commonInit();
}

CPair::CPair(const CPair& pair)
	: first(pair.first), second(pair.second)
{
    this->_commonInit();
}

CPair::CPair(CPair* pair)
	: first(pair->first), second(pair->second)
{
    this->_commonInit();
}

// konstruktor z podanych wartości
CPair::CPair(LONG first, LONG second)
	: first(first), second(second)
{
    this->_commonInit();
}

CPair::~CPair() {
}


VOID CPair::swap(CPair* pair) {
    if (this != pair) {
        ::swap(this->first, pair->first);
        ::swap(this->second, pair->second);
    }
}

BOOL CPair::equals(CObject* obj) {
    if (CObject::equals(obj)) {
        return TRUE;
    }

    CPair* p = dynamic_cast<CPair*>(obj);
    if (p) {
        return this->equals(p);
    }

    return FALSE;
}

BOOL CPair::equals(CPair* pair) {
    // jeśli porównujemy te same wskaźniki, to obiekty są równe
    if (this == pair) {
        return TRUE;
    }
    // jeśli są to dwa różne obiekty o tych samych wartościach first i second, to też są równe
    if (this->first == pair->first && this->second == pair->second) {
        return TRUE;
    }

    return FALSE;
}

BOOL CPair::equals(CObjectPair* pair) {
    return this->equals((CPair*)pair);
}

BOOL CPair::equals(CPairExt* pair) {
    return this->equals((CPair*)pair);
}

CPair& CPair::operator=(CPair& pair) {
    this->first = pair.first;
    this->second = pair.second;
    return (*this);
}

BOOL CPair::operator==(CPair& pair) {
    return this->equals(&pair);
}

BOOL CPair::operator!=(CPair& pair) {
    return !this->equals(&pair);
}
// ***********************************************************************************************
// Koniec klasy CPair
// ***********************************************************************************************









// ***********************************************************************************************
// Klasa CObjectPair
// ***********************************************************************************************
VOID CObjectPair::_commonInit() {
    this->setRealClassName(L"CObjectPair");
}

CObjectPair::CObjectPair()
    : CPair()
{
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = FALSE;
}

CObjectPair::CObjectPair(const CObjectPair& pair) {
    this->_commonInit();
    this->first = pair.first;
    this->second = pair.second;
    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = FALSE;
}

// konstruktor z podanych wartości
CObjectPair::CObjectPair(CObject* first, CObject* second, BOOL destroyFirstOnObjectDestroy, BOOL destroySecondOnObjectDestroy)
{
    this->_commonInit();
    this->first = (LONG)first;
    this->second = (LONG)second;
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
}

CObjectPair::~CObjectPair() {
    this->_cleanup();
}

VOID CObjectPair::_cleanup() {
    if (this->_destroyFirstOnObjectDestroy) {
        if (this->first) {
            delete (CObject*)this->first;
            this->first = NULL;
        }
    }

    if (this->_destroySecondOnObjectDestroy) {
        if (this->second) {
            delete (CObject*)this->second;
            this->second = NULL;
        }
    }
}


VOID CObjectPair::swap(CObjectPair* pair) {
    if (this != pair) {
        ::swap(this->first, pair->first);
        ::swap(this->second, pair->second);
        ::swap(this->_destroyFirstOnObjectDestroy, pair->_destroyFirstOnObjectDestroy);
        ::swap(this->_destroySecondOnObjectDestroy, pair->_destroySecondOnObjectDestroy);
    }
}

BOOL CObjectPair::equals(CPair* pair) {
    return CPair::equals(pair);
}

BOOL CObjectPair::equals(CObjectPair* pair) {
    if ((LONG)this == (LONG)pair) {
        return TRUE;
    }

    CObject* first = (CObject*)this->first;
    CObject* second = (CObject*)this->second;

    return first->equals((CObject*)pair->first) && second->equals((CObject*)pair->second);
}

BOOL CObjectPair::equals(CPairExt* pair) {
    if ((LONG)this == (LONG)pair) {
        return TRUE;
    }

    // jeśli przekazana para zawiera jedną z wartości typu LONG, to zwracamy FALSE
    if (pair->firstType == TPairExtValueType_Long || pair->secondType == TPairExtValueType_Long) {
        return FALSE;
    }

    CObject* first = (CObject*)this->first;
    CObject* second = (CObject*)this->second;

    return first->equals((CObject*)pair->first) && second->equals((CObject*)pair->second);
}

BOOL CObjectPair::equals(CObject* obj) {
    if (CObject::equals(obj)) {
        return TRUE;
    }

    CPairExt *pe = dynamic_cast<CPairExt*>(obj);
    if (pe) {
        return this->equals(pe);
    }
    CObjectPair *op = dynamic_cast<CObjectPair*>(obj);
    if (op) {
        return this->equals(op);
    }
    CPair *p = dynamic_cast<CPair*>(obj);
    if (p) {
        return this->equals(p);
    }
    return FALSE;
}

CObjectPair& CObjectPair::operator=(const CObjectPair& pair) {
    // poniższa metoda uwzględnia ustawienia atrybutów this->_destroy[First|Second]OnObjectDestroy
    this->_cleanup();

    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = FALSE;
    this->first = pair.first;
    this->second = pair.second;

    return *this;
}

BOOL CObjectPair::operator==(const CObjectPair& pair) {
    return this->equals((CObjectPair*)&pair);
}

BOOL CObjectPair::operator!=(const CObjectPair& pair) {
    return !this->equals((CObjectPair*)&pair);
}
// ***********************************************************************************************
// Koniec klasy CObjectPair
// ***********************************************************************************************










// ***********************************************************************************************
// Klasa CPairExt
// ***********************************************************************************************
VOID CPairExt::_commonInit() {
    this->setRealClassName(L"CPairExt");
}

// konstruktor domyślny
CPairExt::CPairExt() : CPair() {
    this->_commonInit();
    this->firstType = TPairExtValueType_Long;
    this->secondType = TPairExtValueType_Long;
    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = FALSE;
}

// konstruktor kopiujący
CPairExt::CPairExt(const CPairExt& pair) {
    this->_commonInit();
    this->first = pair.first;
    this->second = pair.second;
    this->firstType = pair.firstType;
    this->secondType = pair.secondType;
    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = FALSE;
}

// konstruktory z podanych wartości
CPairExt::CPairExt(LONG first, LONG second)
    : CPair(first, second)
{
    this->_commonInit();
    this->firstType = TPairExtValueType_Long;
    this->secondType = TPairExtValueType_Long;
    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = FALSE;
}

WINAPI CPairExt::CPairExt(LONG first, CPair* second, BOOL destroySecondOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    this->first = first;
    this->second = (LONG)second;
    this->firstType = TPairExtValueType_Long;
    this->secondType = TPairExtValueType_PPair;
}

WINAPI CPairExt::CPairExt(CPair* first, LONG second, BOOL destroyFirstOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = FALSE;
    this->first = (LONG)first;
    this->second = second;
    this->firstType = TPairExtValueType_PPair;
    this->secondType = TPairExtValueType_Long;
}

WINAPI CPairExt::CPairExt(CPair* first, CPair* second, BOOL destroyFirstOnObjectDestroy, BOOL destroySecondOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    this->first = (LONG)first;
    this->second = (LONG)second;
    this->firstType = TPairExtValueType_PPair;
    this->secondType = TPairExtValueType_PPair;
}

CPairExt::CPairExt(LONG first, CObject* second, BOOL destroySecondOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    this->first = first;
    this->second = (LONG)second;
    this->firstType = TPairExtValueType_Long;
    this->secondType = TPairExtValueType_PObject;
}

CPairExt::CPairExt(CObject* first, LONG second, BOOL destroyFirstOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = FALSE;
    this->first = (LONG)first;
    this->second = second;
    this->firstType = TPairExtValueType_PObject;
    this->secondType = TPairExtValueType_Long;
}

CPairExt::CPairExt(CObject* first, CObject* second, BOOL destroyFirstOnObjectDestroy, BOOL destroySecondOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    this->first = (LONG)first;
    this->second = (LONG)second;
    this->firstType = TPairExtValueType_PObject;
    this->secondType = TPairExtValueType_PObject;
}

CPairExt::CPairExt(CPair* first, CObject* second, BOOL destroyFirstOnObjectDestroy, BOOL destroySecondOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    this->first = (LONG)first;
    this->second = (LONG)second;
    this->firstType = TPairExtValueType_PPair;
    this->secondType = TPairExtValueType_PObject;
}

CPairExt::CPairExt(CObject* first, CPair* second, BOOL destroyFirstOnObjectDestroy, BOOL destroySecondOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    this->first = (LONG)first;
    this->second = (LONG)second;
    this->firstType = TPairExtValueType_PObject;
    this->secondType = TPairExtValueType_PPair;
}

CPairExt::CPairExt(LONG first, CString second) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = FALSE;
    this->_destroySecondOnObjectDestroy = TRUE;
    this->first = first;
    this->second = (LONG)(new CString(second));
    this->firstType = TPairExtValueType_Long;
    this->secondType = TPairExtValueType_PString;
}

CPairExt::CPairExt(CString first, LONG second) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = TRUE;
    this->_destroySecondOnObjectDestroy = FALSE;
    this->first = (LONG)(new CString(first));
    this->second = second;
    this->firstType = TPairExtValueType_PString;
    this->secondType = TPairExtValueType_Long;
}

CPairExt::CPairExt(CString first, CString second) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = TRUE;
    this->_destroySecondOnObjectDestroy = TRUE;
    this->first = (LONG)(new CString(first));
    this->second = (LONG)(new CString(second));
    this->firstType = TPairExtValueType_PString;
    this->secondType = TPairExtValueType_PString;
}

CPairExt::CPairExt(CPair* first, CString second, BOOL destroyFirstOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = TRUE;
    this->first = (LONG)first;
    this->second = (LONG)(new CString(second));
    this->firstType = TPairExtValueType_PPair;
    this->secondType = TPairExtValueType_PString;
}

CPairExt::CPairExt(CString first, CPair* second, BOOL destroySecondOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = TRUE;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    this->first = (LONG)(new CString(first));
    this->second = (LONG)second;
    this->firstType = TPairExtValueType_PString;
    this->secondType = TPairExtValueType_PPair;
}

CPairExt::CPairExt(CObject* first, CString second, BOOL destroyFirstOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    this->_destroySecondOnObjectDestroy = TRUE;
    this->first = (LONG)first;
    this->second = (LONG)(new CString(second));
    this->firstType = TPairExtValueType_PObject;
    this->secondType = TPairExtValueType_PString;
}

CPairExt::CPairExt(CString first, CObject* second, BOOL destroySecondOnObjectDestroy) {
    this->_commonInit();
    this->_destroyFirstOnObjectDestroy = TRUE;
    this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    this->first = (LONG)(new CString(first));
    this->second = (LONG)second;
    this->firstType = TPairExtValueType_PString;
    this->secondType = TPairExtValueType_PObject;
}


// destruktor
CPairExt::~CPairExt() {
    this->_cleanup();
}

VOID CPairExt::_cleanup() {
    this->deleteFirst();
    this->deleteSecond();
}

VOID CPairExt::deleteFirst() {
    if (this->_destroyFirstOnObjectDestroy) {
        if (this->first && this->firstType != TPairExtValueType_Long) {
            delete (CObject*)this->first;
        }
    }
    this->first = (LONG)NULL;
    this->firstType = TPairExtValueType_Null;
}

VOID CPairExt::deleteSecond() {
    if (this->_destroySecondOnObjectDestroy) {
        if (this->second && this->secondType != TPairExtValueType_Long) {
            delete (CObject*)this->second;
        }
    }
    this->second = (LONG)NULL;
    this->secondType = TPairExtValueType_Null;
}

VOID CPairExt::setFirst(LONG first) {
    this->deleteFirst();
    this->first = first;
    this->firstType = TPairExtValueType_Long;
    this->_destroyFirstOnObjectDestroy = FALSE;
}

VOID CPairExt::setFirst(CObject* first, BOOL destroyFirstOnObjectDestroy) {
    if (!first) {
        this->setFirst((LONG)0);
        return;
    }

    this->deleteFirst();

    CString* first_as_string = dynamic_cast<CString*>(first);
    CPair* first_as_pair = dynamic_cast<CPair*>(first);

    this->first = (LONG)first;

    if (first_as_string) {
        this->firstType = TPairExtValueType_PString;
        this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    }

    else if (first_as_pair) {
        this->firstType = TPairExtValueType_PPair;
        this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    }

    else {
        this->firstType = TPairExtValueType_PObject;
        this->_destroyFirstOnObjectDestroy = destroyFirstOnObjectDestroy;
    }
}

VOID CPairExt::setFirst(CString first) {
    this->deleteFirst();
    this->first = (LONG)(new CString(first.getBuffer()));
    this->firstType = TPairExtValueType_PString;
    this->_destroyFirstOnObjectDestroy = FALSE;
}

VOID CPairExt::setSecond(LONG second) {
    this->deleteSecond();
    this->second = second;
    this->secondType = TPairExtValueType_Long;
    this->_destroySecondOnObjectDestroy = FALSE;
}

VOID CPairExt::setSecond(CObject* second, BOOL destroySecondOnObjectDestroy) {
    if (!second) {
        this->setSecond((LONG)0);
        return;
    }

    this->deleteSecond();

    CString* second_as_string = dynamic_cast<CString*>(second);
    CPair* second_as_pair = dynamic_cast<CPair*>(second);

    this->second = (LONG)second;

    if (second_as_string) {
        this->secondType = TPairExtValueType_PString;
        this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    }

    else if (second_as_pair) {
        this->secondType = TPairExtValueType_PPair;
        this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    }

    else {
        this->secondType = TPairExtValueType_PObject;
        this->_destroySecondOnObjectDestroy = destroySecondOnObjectDestroy;
    }
}
VOID CPairExt::setSecond(CString second) {
    this->deleteSecond();
    this->second = (LONG)(new CString(second.getBuffer()));
    this->secondType = TPairExtValueType_PString;
    this->_destroySecondOnObjectDestroy = FALSE;
}

VOID CPairExt::swap(CPairExt* pair) {
    if (this != pair) {
        ::swap(this->first, pair->first);
        ::swap(this->firstType, pair->firstType);
        ::swap(this->second, pair->second);
        ::swap(this->secondType, pair->secondType);
        ::swap(this->_destroyFirstOnObjectDestroy, pair->_destroyFirstOnObjectDestroy);
        ::swap(this->_destroySecondOnObjectDestroy, pair->_destroySecondOnObjectDestroy);
    }
}

BOOL CPairExt::equals(CPair* pair) {
    return CPair::equals(pair);
}

BOOL CPairExt::equals(CObjectPair* pair) {
    if ((LONG)this == (LONG)pair) {
        return TRUE;
    }

    // jeśli przekazana para zawiera jedną z wartości typu LONG, to zwracamy FALSE
    if (this->firstType == TPairExtValueType_Long || this->secondType == TPairExtValueType_Long) {
        return FALSE;
    }

    CObject* first = (CObject*)pair->first;
    CObject* second = (CObject*)pair->second;

    return first->equals((CObject*)this->first) && second->equals((CObject*)this->second);
}

BOOL CPairExt::equals(CPairExt* pair) {
    if (this == pair) {
        return TRUE;
    }

    BOOL firstElemsEquals = FALSE;
    BOOL secondElemsEquals = FALSE;

    if (this->firstType == pair->firstType) {
        if (this->firstType == TPairExtValueType_Long) {
            firstElemsEquals = this->first == pair->first;
        } else if (this->firstType == TPairExtValueType_PObject) {
            CObject* this_first = (CObject*)this->first;
            CObject* pair_first = (CObject*)pair->first;
            firstElemsEquals = this_first->equals(pair_first);
        } else if (this->firstType == TPairExtValueType_PPair) {
            CPair* this_first = (CPair*)this->first;
            CPair* pair_first = (CPair*)pair->first;
            firstElemsEquals = this_first->equals(pair_first);
        } else if (this->firstType == TPairExtValueType_PString) {
            CString* this_first = (CString*)this->first;
            CString* pair_first = (CString*)pair->first;
            firstElemsEquals = this_first->equals(pair_first);
        }
    }

    if (!firstElemsEquals) {
        return FALSE;
    }


    if (this->secondType == pair->secondType) {
        if (this->secondType == TPairExtValueType_Long) {
            secondElemsEquals = this->second == pair->second;
        } else if (this->secondType == TPairExtValueType_PObject) {
            CObject* this_second = (CObject*)this->second;
            CObject* pair_second = (CObject*)pair->second;
            secondElemsEquals = this_second->equals(pair_second);
        } else if (this->secondType == TPairExtValueType_PPair) {
            CPair* this_second = (CPair*)this->second;
            CPair* pair_second = (CPair*)pair->second;
            secondElemsEquals = this_second->equals(pair_second);
        } else if (this->secondType == TPairExtValueType_PString) {
            CString* this_second = (CString*)this->second;
            CString* pair_second = (CString*)pair->second;
            secondElemsEquals = this_second->equals(pair_second);
        }
    }

    if (!secondElemsEquals) {
        return FALSE;
    }

    return TRUE;
}

BOOL CPairExt::equals(CObject* obj) {
    if (CObject::equals(obj)) {
        return TRUE;
    }

    CPair* p = dynamic_cast<CPair*>(obj);
    return this->equals(p);
}

CObject* CPairExt::getFirstAsObject() {
    if (this->firstType == TPairExtValueType_PObject) {
        return (CObject*)this->first;
    }
    return NULL;
}
CPair* CPairExt::getFirstAsPair() {
    if (this->firstType == TPairExtValueType_PPair) {
        return (CPair*)this->first;
    }
    return NULL;
}
CObjectPair* CPairExt::getFirstAsObjectPair() {
    if (this->firstType == TPairExtValueType_PPair) {
        return (CObjectPair*)this->first;
    }
    return NULL;
}
CPairExt* CPairExt::getFirstAsPairExt() {
    if (this->firstType == TPairExtValueType_PPair) {
        return (CPairExt*)this->first;
    }
    return NULL;
}
CString* CPairExt::getFirstAsString() {
    if (this->firstType == TPairExtValueType_PString) {
        return (CString*)this->first;
    }
    return NULL;
}


CObject* CPairExt::getSecondAsObject() {
    if (this->secondType == TPairExtValueType_PObject) {
        return (CObject*)this->second;
    }
    return NULL;
}
CPair* CPairExt::getSecondAsPair() {
    if (this->secondType == TPairExtValueType_PPair) {
        return (CPair*)this->second;
    }
    return NULL;
}
CObjectPair* CPairExt::getSecondAsObjectPair() {
    if (this->secondType == TPairExtValueType_PPair) {
        return (CObjectPair*)this->second;
    }
    return NULL;
}
CPairExt* CPairExt::getSecondAsPairExt() {
    if (this->secondType == TPairExtValueType_PPair) {
        return (CPairExt*)this->second;
    }
    return NULL;
}
CString* CPairExt::getSecondAsString() {
    if (this->secondType == TPairExtValueType_PString) {
        return (CString*)this->second;
    }
    return NULL;
}

CPairExt& CPairExt::operator=(const CPairExt& pair) {
    // poniższa metoda uwzględnia ustawienia atrybutów this->_destroy[First|Second]OnObjectDestroy
    this->_cleanup();

    if (pair.firstType == TPairExtValueType_PString) {
        this->_destroyFirstOnObjectDestroy = TRUE;
        this->first = (LONG)(new CString(((CString*)pair.first)->getBuffer()));
        this->firstType = TPairExtValueType_PString;
    } else {
        this->_destroyFirstOnObjectDestroy = FALSE;
        this->first = pair.first;
        this->firstType = pair.firstType;
    }

    if (pair.secondType == TPairExtValueType_PString) {
        this->_destroySecondOnObjectDestroy = TRUE;
        this->second = (LONG)(new CString(((CString*)pair.second)->getBuffer()));
        this->secondType = TPairExtValueType_PString;
    } else {
        this->_destroySecondOnObjectDestroy = FALSE;
        this->second = pair.second;
        this->secondType = pair.secondType;
    }

    return *this;
}

BOOL CPairExt::operator==(const CPairExt& pair) {
    return this->equals((CPairExt*)&pair);
}

BOOL CPairExt::operator!=(const CPairExt& pair) {
    return !this->equals((CPairExt*)&pair);
}

CPairExt* CPairExt::clone() {
    CPairExt* result = new CPairExt((LONG)0, (LONG)0);
    if (result) {
        *result = *this;
    }
    return result;
}
// ***********************************************************************************************
// ***********************************************************************************************










// ***********************************************************************************************
// Klasa CMap
// ***********************************************************************************************
VOID CMap::_commonInit() {
    this->setRealClassName(L"CMap");
}

// konstruktor domyślny
CMap::CMap(ULONG capacity) {
    this->_commonInit();
    if (capacity > 0) {
        this->_items = new CArrayList(capacity);
    } else {
        this->_items = new CArrayList();
    }
}

// konstruktor kopiujący - zablokowany
CMap::CMap(const CMap& map) {
    this->_commonInit();
}

CMap::~CMap() {
    if (this->_items) {
        CPairExt* pair;
        ULONG size = this->_items->size();
        LONG* items = this->_items->getItemsBuf();
        for (ULONG i = 0; i < size; i++) {
            pair = (CPairExt*)items[i];
            delete pair;
            items[i] = (LONG)NULL;
        }
        delete this->_items;
    }
}

VOID CMap::append(CPairExt* pair) {
    this->_items->append((LONG)pair);
}

VOID CMap::push(CPairExt* pair) {
    this->_items->append((LONG)pair);
}

VOID CMap::addAsFirst(CPairExt* pair) {
    this->_items->addAsFirst((LONG)pair);
}

VOID CMap::insert(ULONG pos, CPairExt* pair) {
    this->_items->insert(pos, (LONG)pair);
}

ULONG CMap::del(CPairExt* pair) {
    // Musimy ręcznie iterować po tablicy, bo oprócz wyrzucenia z niej wskaźnika trzeba
    // jeszcze zrobić delete na obiekcie na który wskazuje
    LONG pair_long = (LONG)pair;
    LONG p;
    ULONG deleted = 0;
    ULONG size = this->_items->size();
    if (size < 1) {
        return 0;
    }
    LONG* items = this->_items->getItemsBuf();

    for (ULONG i = 0; i < size; i++) {
        p = items[i];
        if (p == pair_long) {
            delete (CPairExt*)p;
            deleted++;
            items[i] = (LONG)NULL;
        }
    }
    if (deleted > 0) {
        // usuwamy z tablicy wszystkie NULL-e
        this->_items->del((LONG)NULL);
    }
    return deleted;
}

BOOL CMap::delFirst(CPairExt* pair) {
    LONG p;
    LONG pair_long = (LONG)pair;
    ULONG size = this->_items->size();
    LONG* items = this->_items->getItemsBuf();

    for (ULONG i = 0; i < size; i++) {
        p = items[i];
        if (p == pair_long) {
            delete (CPairExt*)p;
            this->_items->delAt(i);
            return TRUE;
        }
    }
    return FALSE;
}

BOOL CMap::delAt(ULONG pos) {
    CPairExt* p = (CPairExt*)this->_items->getAt(pos);
    if (p) {
        delete p;
        return this->_items->delAt(pos);
    }
    return FALSE;
}

LONG CMap::pos(CPairExt* pair) {
    LONG p;
    LONG pair_long = (LONG)pair;
    ULONG size = this->_items->size();
    LONG* items = this->_items->getItemsBuf();

    for (ULONG i = 0; i < size; i++) {
        p = items[i];
        if (p == pair_long) {
            return i;
        }
    }
    return -1;
}

CPairExt* CMap::getAt(ULONG pos) {
    if (pos < this->_items->size())
        return (CPairExt*)this->_items->getAt(pos);

    return NULL;
}

CPairExt* CMap::getFirst() {
    if (this->_items->size() > 0)
        return (CPairExt*)this->_items->getAt(0);

    return NULL;
}

CPairExt* CMap::getLast() {
    ULONG size = this->_items->size();
    if (size > 0) {
        return (CPairExt*)this->_items->getAt(size - 1);
    }

    return NULL;
}

CPairExt* CMap::pop() {
    return (CPairExt*)this->_items->pop();
}

VOID CMap::clear() {
    if (this->_items) {
        CPairExt* p;
        ULONG size = this->_items->size();
        LONG* items = this->_items->getItemsBuf();

        for (ULONG i = 0; i < size; i++) {
            p = (CPairExt*)items[i];
            delete p;
            items[i] = (LONG)NULL;
        }
        this->_items->clear();
    }
}

CPairExt* CMap::findFirstItemByFirst(LONG first, LONG* foundPosition) {
    CPairExt* p;
    ULONG size = this->_items->size();
    if (size < 1) {
        *foundPosition = -1;
        return NULL;
    }
    LONG* items = this->_items->getItemsBuf();

    for (ULONG i = 0; i < size; i++) {
        p = (CPairExt*)items[i];
        if (p->first == first && p->firstType == TPairExtValueType_Long) {
            *foundPosition = i;
            return p;
        }
    }

    *foundPosition = -1;
    return NULL;
}

CPairExt* CMap::findFirstItemByFirst(CString key, LONG* foundPosition) {
    CPairExt* p;
    ULONG size = this->_items->size();
    if (size < 1) {
        *foundPosition = -1;
        return NULL;
    }
    LONG* items = this->_items->getItemsBuf();

    for (ULONG i = 0; i < size; i++) {
        p = (CPairExt*)items[i];
        if (p->firstType == TPairExtValueType_PString) {
            CString* s = (CString*)p->first;
            if (s && *s == key) {
                *foundPosition = i;
                return p;
            }
        }
    }

    *foundPosition = -1;
    return NULL;
}

CArrayList* CMap::findAllItemsByFirst(LONG first) {
    CPairExt* p;
    ULONG size = this->_items->size();
    if (size < 1) {
        return NULL;
    }
    CArrayList* result = NULL;
    LONG* items = this->_items->getItemsBuf();

    for (ULONG i = 0; i < size; i++) {
        p = (CPairExt*)items[i];
        if (p->first == first && p->firstType == TPairExtValueType_Long) {
            if (!result) {
                result = new CArrayList(this->size());
            }
            result->append((LONG)p);
        }
    }

    return result;
}

CPairExt* CMap::findFirstItemByFirst(CObject* first, LONG* foundPosition) {
    CPairExt* p;
    ULONG size = this->_items->size();
    if (size < 1) {
        *foundPosition = -1;
        return NULL;
    }

    CObject* first_as_object = first;
    CPair* first_as_pair = dynamic_cast<CPair*>(first);
    CString* first_as_string = dynamic_cast<CString*>(first);

    LONG* items = this->_items->getItemsBuf();

    for (ULONG i = 0; i < size; i++) {
        p = (CPairExt*)items[i];
        if (first_as_string && p->firstType == TPairExtValueType_PString && first_as_string->equals((CObject*)p->first)) {
            *foundPosition = i;
            return p;
        }
        else if (first_as_pair && p->firstType == TPairExtValueType_PPair) {
            CPair* p_first_as_pair = dynamic_cast<CPair*>((CObject*)p->first);
            CObjectPair* p_first_as_objectpair = dynamic_cast<CObjectPair*>((CObject*)p->first);
            CPairExt* p_first_as_pairext = dynamic_cast<CPairExt*>((CObject*)p->first);

            if (p_first_as_pairext && first_as_pair->equals(p_first_as_pairext)) {
                *foundPosition = i;
                return p;
            }
            else if (p_first_as_objectpair && first_as_pair->equals(p_first_as_objectpair)) {
                *foundPosition = i;
                return p;
            }
            else if (p_first_as_pair && first_as_pair->equals(p_first_as_pair)) {
                *foundPosition = i;
                return p;
            }
        }
        else if (first_as_object && p->firstType == TPairExtValueType_PObject && first_as_object->equals((CObject*)p->first)) {
            *foundPosition = i;
            return p;
        }
    }

    *foundPosition = -1;
    return NULL;
}

CArrayList* CMap::findAllItemsByFirst(CObject* first) {
    CPairExt* p;
    ULONG size = this->_items->size();
    if (size < 1) {
        return NULL;
    }

    CArrayList* result = NULL;
    CObject* first_as_object = first;
    CPair* first_as_pair = dynamic_cast<CPair*>(first);
    CString* first_as_string = dynamic_cast<CString*>(first);

    LONG* items = this->_items->getItemsBuf();

    for (ULONG i = 0; i < size; i++) {
        p = (CPairExt*)items[i];
        if (first_as_string && p->firstType == TPairExtValueType_PString && first_as_string->equals((CObject*)p->first)) {
            if (!result) {
                result = new CArrayList(this->size());
            }
            result->append((LONG)p);
            continue;
        }
        else if (first_as_pair && p->firstType == TPairExtValueType_PPair) {
            CPair* p_first_as_pair = dynamic_cast<CPair*>((CObject*)p->first);
            CObjectPair* p_first_as_objectpair = dynamic_cast<CObjectPair*>((CObject*)p->first);
            CPairExt* p_first_as_pairext = dynamic_cast<CPairExt*>((CObject*)p->first);

            if (p_first_as_pairext && first_as_pair->equals(p_first_as_pairext)) {
                if (!result) {
                    result = new CArrayList(this->size());
                }
                result->append((LONG)p);
                continue;
            }
            else if (p_first_as_objectpair && first_as_pair->equals(p_first_as_objectpair)) {
                if (!result) {
                    result = new CArrayList(this->size());
                }
                result->append((LONG)p);
                continue;
            }
            else if (p_first_as_pair && first_as_pair->equals(p_first_as_pair)) {
                if (!result) {
                    result = new CArrayList(this->size());
                }
                result->append((LONG)p);
                continue;
            }
        }
        else if (first_as_object && p->firstType == TPairExtValueType_PObject && first_as_object->equals((CObject*)p->first)) {
            if (!result) {
                result = new CArrayList(this->size());
            }
            result->append((LONG)p);
            continue;
        }
    }

    return result;
}

BOOL CMap::erase(LONG first) {
    LONG position;
    CPairExt* p = this->findFirstItemByFirst(first, &position);
    if (p) {
        return this->delAt(position);
    }

    return FALSE;
}

BOOL CMap::erase(CObject* first) {
    LONG position;
    CPairExt* p = this->findFirstItemByFirst(first, &position);
    if (p) {
        return this->delAt(position);
    }

    return FALSE;
}

ULONG CMap::remove(LONG first) {
    CPairExt* p;
    ULONG deleted = 0;
    ULONG size = this->_items->size();
    if (size < 1) {
        return 0;
    }
    LONG* items = this->_items->getItemsBuf();
    for (ULONG i = 0; i < size; i++) {
        p = (CPairExt*)items[i];
        if (p && p->first == first) {
            delete p;
            deleted++;
            items[i] = 0;
        }
    }
    if (deleted > 0) {
        // usuwamy z tablicy wszystkie NULL-e
        this->_items->del(0);
    }
    return deleted;
}

ULONG CMap::remove(CString second) {
    CPairExt* p;
    ULONG deleted = 0;
    ULONG size = this->_items->size();
    if (size < 1) {
        return 0;
    }
    LONG* items = this->_items->getItemsBuf();
    for (ULONG i = 0; i < size; i++) {
        p = (CPairExt*)items[i];
        if (p->secondType == TPairExtValueType_PString && p->getSecondAsString()->equals(&second)) {
            delete p;
            deleted++;
            items[i] = 0;
        }
    }
    if (deleted > 0) {
        // usuwamy z tablicy wszystkie NULL-e
        this->_items->del(0);
    }
    return deleted;
}

LONG CMap::get(LONG first, CPairExt** pair) {
    if (pair) {
        *pair = NULL;
    }

    if (this->_items) {
        CPairExt* p;
        ULONG size = this->_items->size();
        LONG* items = this->_items->getItemsBuf();

        for (ULONG i = 0; i < size; i++) {
            p = (CPairExt*)items[i];
            if (p->first == first) {
                if (pair) {
                    *pair = p;
                }
                return p->second;
            }
        }
    }

    return MININT;
}

CObject* CMap::getAsObject(LONG first, CPairExt** pair) {
    if (pair) {
        *pair = NULL;
    }
    if (!this->_items) {
        return NULL;
    }

    CPairExt* pairOut = NULL;
    LONG second = this->get(first, &pairOut);
    if (pair) {
        *pair = pairOut;
    }
    if (second != MININT) {
        if (pairOut && pairOut->secondType == TPairExtValueType_PObject) {
            return (CObject*)second;
        }
    }

    return NULL;
}

CPair* CMap::getAsPair(LONG first, CPairExt** pair) {
    if (pair) {
        *pair = NULL;
    }
    if (!this->_items) {
        return NULL;
    }

    CPairExt* pairOut = NULL;
    LONG second = this->get(first, &pairOut);
    if (pair) {
        *pair = pairOut;
    }
    if (second != MININT) {
        if (pairOut && pairOut->secondType == TPairExtValueType_PPair) {
            return (CPair*)second;
        }
    }

    return NULL;
}

CObjectPair* CMap::getAsObjectPair(LONG first, CPairExt** pair) {
    if (pair) {
        *pair = NULL;
    }
    if (!this->_items) {
        return NULL;
    }

    CPairExt* pairOut = NULL;
    LONG second = this->get(first, &pairOut);
    if (pair) {
        *pair = pairOut;
    }
    if (second != MININT) {
        if (pairOut && pairOut->secondType == TPairExtValueType_PPair) {
            CObjectPair* p_obj = dynamic_cast<CObjectPair*>((CPair*)second);
            if (p_obj) {
                return p_obj;
            }
        }
    }

    return NULL;
}

CPairExt* CMap::getAsPairExt(LONG first, CPairExt** pair) {
    CPairExt* pairOut = NULL;
    LONG second = this->get(first, &pairOut);
    if (pair) {
        *pair = pairOut;
    }
    if (second != MININT) {
        if (pairOut && pairOut->secondType == TPairExtValueType_PPair) {
            CPairExt* p_ext = dynamic_cast<CPairExt*>((CPair*)second);
            if (p_ext) {
                return p_ext;
            }
        }
    }

    return NULL;
}

LONG CMap::get(CObject* first, CPairExt** pair) {
    LONG position;
    CPairExt* pair_out = this->findFirstItemByFirst(first, &position);
    if (pair_out) {
        if (pair) {
            *pair = pair_out;
        }
        return pair_out->second;
    }

    if (pair) {
        *pair = NULL;
    }
    return MININT;
}

CObject* CMap::getAsObject(CObject* first, CPairExt** pair) {
    LONG position;
    CPairExt* pair_out = this->findFirstItemByFirst(first, &position);
    if (pair_out) {
        if (pair) {
            *pair = pair_out;
        }
        return pair_out->getSecondAsObject();
    }

    if (pair) {
        *pair = NULL;
    }
    return NULL;
}

CPair* CMap::getAsPair(CObject* first, CPairExt** pair) {
    LONG position;
    CPairExt* pair_out = this->findFirstItemByFirst(first, &position);
    if (pair_out) {
        if (pair) {
            *pair = pair_out;
        }
        return pair_out->getFirstAsPair();
    }

    if (pair) {
        *pair = NULL;
    }
    return NULL;
}

CObjectPair* CMap::getAsObjectPair(CObject* first, CPairExt** pair) {
    LONG position;
    CPairExt* pair_out = this->findFirstItemByFirst(first, &position);
    if (pair_out) {
        if (pair) {
            *pair = pair_out;
        }
        return pair_out->getSecondAsObjectPair();
    }

    if (pair) {
        *pair = NULL;
    }
    return NULL;
}

CPairExt* CMap::getAsPairExt(CObject* first, CPairExt** pair) {
    LONG position;
    CPairExt* pair_out = this->findFirstItemByFirst(first, &position);
    if (pair_out) {
        if (pair) {
            *pair = pair_out;
        }
        return pair_out->getSecondAsPairExt();
    }

    if (pair) {
        *pair = NULL;
    }
    return NULL;
}

BOOL CMap::set(LONG first, LONG second) {
    LONG position;
    CPairExt* pair = this->findFirstItemByFirst(first, &position);
    if (pair != NULL) {
        pair->setSecond(second);
        return TRUE;
    }

    pair = new CPairExt(first, second);
    if (pair) {
        this->append(pair);
        return TRUE;
    }

    return FALSE;
}

BOOL CMap::set(LONG first, CObject* second) {
    LONG position;
    CPairExt* pair = this->findFirstItemByFirst(first, &position);
    if (pair != NULL) {
        pair->setSecond(second);
        return TRUE;
    }

    pair = new CPairExt(first, second);
    if (pair) {
        this->append(pair);
        return TRUE;
    }

    return FALSE;
}

BOOL CMap::set(LONG first, CString second) {
    LONG position;
    CPairExt* pair = this->findFirstItemByFirst(first, &position);
    if (pair != NULL) {
        pair->setSecond(second);
        return TRUE;
    }

    pair = new CPairExt(first, second);
    if (pair) {
        this->append(pair);
        return TRUE;
    }

    return FALSE;
}

BOOL CMap::set(CObject* first, LONG second) {
    LONG position;
    CPairExt* pair = this->findFirstItemByFirst(first, &position);
    if (pair != NULL) {
        pair->setSecond(second);
        return TRUE;
    }

    pair = new CPairExt(first, second);
    if (pair) {
        this->append(pair);
        return TRUE;
    }

    return FALSE;
}

BOOL CMap::set(CObject* first, CObject* second) {
    LONG position;
    CPairExt* pair = this->findFirstItemByFirst(first, &position);
    if (pair != NULL) {
        pair->setSecond(second);
        return TRUE;
    }

    pair = new CPairExt(first, second);
    if (pair) {
        this->append(pair);
        return TRUE;
    }

    return FALSE;
}

BOOL CMap::set(CObject* first, CString second) {
    LONG position;
    CPairExt* pair = this->findFirstItemByFirst(first, &position);
    if (pair != NULL) {
        pair->setSecond(second);
        return TRUE;
    }

    pair = new CPairExt(first, second);
    if (pair) {
        this->append(pair);
        return TRUE;
    }

    return FALSE;
}

LONG CMap::count(LONG first) {
    LONG result = 0;
    CArrayList* found_items = this->findAllItemsByFirst(first);
    if (found_items) {
        result = found_items->size();
        deleteArrayListOfPairExt(found_items);
        found_items = NULL;
    }
    return result;
}

LONG CMap::count(CPair* first) {
    LONG result = 0;
    CArrayList* found_items = this->findAllItemsByFirst(first);
    if (found_items) {
        result = found_items->size();
        deleteArrayListOfPairExt(found_items);
        found_items = NULL;
    }
    return result;
}

VOID CMap::set(CString key, CString value) {
    LONG position;
    CPairExt* pair = this->findFirstItemByFirst(key, &position);
    if (pair != NULL) {
        pair->setSecond(value);
        return;
    }

    pair = new CPairExt(key, value);
    if (pair) {
        this->append(pair);
    }
}

CString CMap::get(CString key) {
    LONG position;
    CPairExt* pair = this->findFirstItemByFirst(key, &position);
    if (pair != NULL) {
        CString* s = pair->getSecondAsString();
        if (s) {
            return *s;
        }
    }

    return COMMON_EMPTY_STRING;
}

CStringList* CMap::getKeys() {
    CStringList* lst = new CStringList();
    if (lst) {
        CPairExt* p;
        ULONG size = this->_items->size();
        if (size < 1) {
            return lst;
        }
        
        LONG* items = this->_items->getItemsBuf();
        CString* s;
        for (ULONG i = 0; i < size; i++) {
            p = (CPairExt*)items[i];
            if (p->firstType == TPairExtValueType_PString) {
                s = (CString*)p->first;
                lst->append(*s);
            }
        }
    }
    
    return lst;
}

CStringList* CMap::getValues() {
    CStringList* lst = new CStringList();
    if (lst) {
        CPairExt* p;
        ULONG size = this->_items->size();
        if (size < 1) {
            return lst;
        }
        
        LONG* items = this->_items->getItemsBuf();
        CString* s;
        for (ULONG i = 0; i < size; i++) {
            p = (CPairExt*)items[i];
            if (p->secondType == TPairExtValueType_PString) {
                s = (CString*)p->second;
                lst->append(*s);
            }
        }
    }
    
    return lst;
}

CMap* CMap::clone() {
    CMap* retval = new CMap();
    if (retval) {
        CPairExt* p;
        CPairExt* clonedP;
        ULONG size = this->_items->size();
        if (size > 0) {
            LONG* items = this->_items->getItemsBuf();
            CString* s;
            for (ULONG i = 0; i < size; i++) {
                p = (CPairExt*)items[i];
                if (p) {
                    clonedP = p->clone();
                    retval->_items->append((LONG)clonedP);
                }
            }
        }
    }
    
    return retval;
}

// ***********************************************************************************************
// ***********************************************************************************************
