/*
    TCFbECachedTable.cpp    November 27, 2004.

    Copyright (C) 2003-2004 CFbE Research Group,
    Software Engineering Laboratory,
    Graduate School of Information Science,
    Nara Institute of Science and Technology,
    All rights reserved.

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, (at your option) or
    any later version.

    This program 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
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GNU Emacs; see the file COPYING.  If not, write to the
    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/
//---------------------------------------------------------------------------
#pragma hdrstop

#include "TCFbECachedTable.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
// コンストラクタ（空キャッシュを作成
__fastcall TCFbECachedTable::TCFbECachedTable(void)
    : TCFbEPrimitiveTable()
{
    this->ChildCacheList = new TObjectList();
    this->ChildCacheLabelList = new THashedStringList();
    this->FIsLinkedToParent = false;

    this->InitCache();
}

//---------------------------------------------------------------------------
// ParentTable の子テーブルを生成する
__fastcall TCFbECachedTable::TCFbECachedTable(TCFbECachedTable* ParentTable)
    : TCFbEPrimitiveTable()
{
    this->ChildCacheList = new TObjectList();
    this->ChildCacheLabelList = new THashedStringList();
    this->FIsLinkedToParent = false;

    for (int i = 0; i < ParentTable->GetColumnLabelList()->Count; i++) {
        this->ParentAddColumn(ParentTable->GetColumnLabelList()->Strings[i].c_str());
    }

    for (int j = 0; j < ParentTable->GetRowLabelList()->Count; j++) {
        this->ParentAddRow(ParentTable->GetRowLabelList()->Strings[j].c_str());
    }

    this->InitCache();
}

//---------------------------------------------------------------------------
// 指定された行列を備えた空テーブルを作成
__fastcall TCFbECachedTable::TCFbECachedTable(TStringList* RowLabelList, TStringList* ColumnLabelList)
    : TCFbEPrimitiveTable(RowLabelList, ColumnLabelList)
{
    this->ChildCacheList = new TObjectList();
    this->ChildCacheLabelList = new THashedStringList();
    this->FIsLinkedToParent = false;

    this->InitCache();
}

//---------------------------------------------------------------------------
// コンストラクタ
__fastcall TCFbECachedTable::TCFbECachedTable(TStringList* SourceCsvStringList)
    : TCFbEPrimitiveTable(SourceCsvStringList)
{
    this->ChildCacheList = new TObjectList();
    this->ChildCacheLabelList = new THashedStringList();
    this->FIsLinkedToParent = false;

    this->InitCache();
}

//---------------------------------------------------------------------------
// デストラクタ
__fastcall TCFbECachedTable::~TCFbECachedTable()
{
    delete this->ChildCacheLabelList;
    delete this->ChildCacheList;
}

//---------------------------------------------------------------------------
void __fastcall TCFbECachedTable::InitCache(void)
{
    for (int i = 0; i < this->GetColumnLabelList()->Count; i++) {
        for (int k = 0; k < this->ChildCacheList->Count; k++) {
            this->GetChildCacheElement(k)->ParentAddColumn(this->GetColumnLabelList()->Strings[i].c_str());
        }
    }

    for (int j = 0; j < this->GetRowLabelList()->Count; j++) {
        for (int k = 0; k < this->ChildCacheList->Count; k++) {
            this->GetChildCacheElement(k)->ParentAddRow(this->GetRowLabelList()->Strings[j].c_str());
        }
    }
}

//---------------------------------------------------------------------------
// SourceTable の各要素をコピーする
void __fastcall TCFbECachedTable::Assign(TCFbEPrimitiveTable* SourceTable)
{
    this->CopyAllElementsAndCache((TCFbECachedTable*)SourceTable, true, true);
}

//---------------------------------------------------------------------------
// SourceTable の各要素の行と列を入替えてコピーする
void __fastcall TCFbECachedTable::RotatedAssign(TCFbEPrimitiveTable* SourceTable)
{
    this->RotatedCopyAllElementsAndCache((TCFbECachedTable*)SourceTable, true, true);
}

//---------------------------------------------------------------------------
void __fastcall TCFbECachedTable::CopyAllElementsAndCache(TCFbECachedTable* SourceTable, bool AddLackCache, bool DeleteExcessiveCache)
{
    TCFbEPrimitiveTable::Assign(SourceTable);
    TCFbECachedTable* SourceCachedTable = (TCFbECachedTable*)SourceTable;

    // 余分なキャッシュを削除する
    int i = 0;
    if (DeleteExcessiveCache) {
        while (i < this->ChildCacheLabelList->Count) {
            if (SourceCachedTable->GetChildCache(this->ChildCacheLabelList->Strings[i].c_str()) == NULL) {
                this->ChildCacheLabelList->Delete(i);
                this->ChildCacheList->Delete(i);
            } else {
                i++;
            }
        }
    }

    // 足りないキャッシュを追加し，キャッシュの内容をコピーする
    if (AddLackCache) {
        for (i = 0; i < SourceCachedTable->ChildCacheLabelList->Count; i++) {
            TCFbECachedTable* SrcCache = (TCFbECachedTable*)SourceCachedTable->ChildCacheList->Items[i];
            TCFbECachedTable* DestCache = this->GetChildCache(SourceCachedTable->ChildCacheLabelList->Strings[i].c_str());
            if (DestCache == NULL) {
                this->AddChildCache(SrcCache->CreateClone(), SourceCachedTable->ChildCacheLabelList->Strings[i].c_str(), SrcCache->IsLinkedToParent());
            } else {
                DestCache->Assign(SrcCache);
            }
        }
    }

    this->FIsLinkedToParent = SourceTable->FIsLinkedToParent;
}

//---------------------------------------------------------------------------
void __fastcall TCFbECachedTable::RotatedCopyAllElementsAndCache(TCFbECachedTable* SourceTable, bool AddLackCache, bool DeleteExcessiveCache)
{
    TCFbEPrimitiveTable::RotatedAssign(SourceTable);
    TCFbECachedTable* SourceCachedTable = (TCFbECachedTable*)SourceTable;

    // 余分なキャッシュを削除する
    int i = 0;
    if (DeleteExcessiveCache) {
        while (i < this->ChildCacheLabelList->Count) {
            if (SourceCachedTable->GetChildCache(this->GetRotatedChildCacheName(this->ChildCacheLabelList->Strings[i].c_str()).c_str()) == NULL) {
                this->ChildCacheLabelList->Delete(i);
                this->ChildCacheList->Delete(i);
            } else {
                i++;
            }
        }
    }

    // 足りないキャッシュを追加し，キャッシュの内容をコピーする
    if (AddLackCache) {
        for (i = 0; i < SourceCachedTable->ChildCacheLabelList->Count; i++) {
            TCFbECachedTable* SrcCache = (TCFbECachedTable*)SourceCachedTable->ChildCacheList->Items[i];
            AnsiString RotatedChildCacheName = this->GetRotatedChildCacheName(SourceCachedTable->ChildCacheLabelList->Strings[i].c_str());
            TCFbECachedTable* DestCache = this->GetChildCache(RotatedChildCacheName.c_str());
            if (DestCache == NULL) {
                this->AddChildCache(SrcCache->CreateRotatedClone(), RotatedChildCacheName.c_str(), SrcCache->IsLinkedToParent());
            } else {
                DestCache->RotatedAssign(SrcCache);
            }
        }
    }
    
    this->FIsLinkedToParent = SourceTable->FIsLinkedToParent;
}

//---------------------------------------------------------------------------
// ChildCache を ChildCacheName という名前のキャッシュとして追加する
void __fastcall TCFbECachedTable::AddChildCache(TCFbECachedTable* ChildCache, const char* ChildCacheName, bool IsLinkedToParent)
{
    if (this->ChildCacheLabelList->IndexOf(ChildCacheName) != -1) {
        return;
    }

    this->ChildCacheLabelList->Add(ChildCacheName);
    this->ChildCacheList->Add(ChildCache);
    ChildCache->FIsLinkedToParent = IsLinkedToParent;
}

//---------------------------------------------------------------------------
// 親キャッシュのセルが更新された
void __fastcall TCFbECachedTable::ParentCellIsUpdated(int RowIndex, int ColumnIndex)
{
    if (this->FIsLinkedToParent) {
        this->UnableElementByIndex(RowIndex, ColumnIndex);
    }
}

//---------------------------------------------------------------------------
// 以下，protected method
//---------------------------------------------------------------------------
// ChildCacheName という名前のキャッシュの行と列を入れ替えたキャッシュの名前を取得する
const AnsiString __fastcall TCFbECachedTable::GetRotatedChildCacheName(const char* ChildCacheName)
{
    AnsiString CurrentExt = ExtractFileExt(ChildCacheName).LowerCase();
    if (CurrentExt == ".row") {
        return ChangeFileExt(ChildCacheName, ".column");
    } else if (CurrentExt == ".column") {
        return ChangeFileExt(ChildCacheName, ".row");
    }
    return ChildCacheName;
}

//---------------------------------------------------------------------------
// 行を削除する．
void __fastcall TCFbECachedTable::DeleteRowByIndex(int TargetRowIndex)
{
    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        if (this->GetEnabledByIndex(TargetRowIndex, j)) {
            this->CellIsUpdatedByIndex(TargetRowIndex, j);
        }
    }
    TCFbEPrimitiveTable::DeleteRowByIndex(TargetRowIndex);
    for (int i = 0; i < this->ChildCacheList->Count; i++) {
        this->GetChildCacheElement(i)->ParentDeleteRowByIndex(TargetRowIndex);
    }
}

//---------------------------------------------------------------------------
// 列を削除する．
void __fastcall TCFbECachedTable::DeleteColumnByIndex(int TargetColumnIndex)
{
    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        if (this->GetEnabledByIndex(i, TargetColumnIndex)) {
            this->CellIsUpdatedByIndex(i, TargetColumnIndex);
        }
    }
    TCFbEPrimitiveTable::DeleteColumnByIndex(TargetColumnIndex);
    for (int i = 0; i < this->ChildCacheList->Count; i++) {
        this->GetChildCacheElement(i)->ParentDeleteColumnByIndex(TargetColumnIndex);
    }
}

//---------------------------------------------------------------------------
int __fastcall TCFbECachedTable::AddRow(const char* RowLabel)
{
    for (int i = 0; i < this->ChildCacheList->Count; i++) {
        this->GetChildCacheElement(i)->ParentAddRow(RowLabel);
    }
    return TCFbEPrimitiveTable::AddRow(RowLabel);
}

//---------------------------------------------------------------------------
int __fastcall TCFbECachedTable::AddColumn(const char* ColumnLabel)
{
    for (int i = 0; i < this->ChildCacheList->Count; i++) {
        this->GetChildCacheElement(i)->ParentAddColumn(ColumnLabel);
    }
    return TCFbEPrimitiveTable::AddColumn(ColumnLabel);
}

//---------------------------------------------------------------------------
void __fastcall TCFbECachedTable::CellIsUpdatedByIndex(int RowIndex, int ColumnIndex)
{
    for (int i = 0; i < this->ChildCacheList->Count; i++) {
        this->GetChildCacheElement(i)->ParentCellIsUpdated(RowIndex, ColumnIndex);
    }
}

//---------------------------------------------------------------------------

