/*
    TCFbEPrimitiveTable.cpp    November 26, 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

#define BUFFERLEN    256

#include "TCFbEPrimitiveTable.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
// コンストラクタ（空のテーブルを作成
__fastcall TCFbEPrimitiveTable::TCFbEPrimitiveTable(void)
{
    this->FRowLabelList = new THashedStringList();
    this->FColumnLabelList = new THashedStringList();
    this->RowList = new TObjectList();
}

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

    for (int j = 0; j < ColumnLabelList->Count; j++) {
        TCFbEPrimitiveTable::AddColumn(ColumnLabelList->Strings[j].c_str());
    }

    for (int i = 0; i < RowLabelList->Count; i++) {
        TCFbEPrimitiveTable::AddRow(RowLabelList->Strings[i].c_str());
    }
}

//---------------------------------------------------------------------------
// コンストラクタ
// SourceCsvStringList からデータを構築する
// Row の 1 行目にレコードのラベルが
// Column の 1 行目にアイテムのラベルが
// 後は値が入っているとみなす
__fastcall TCFbEPrimitiveTable::TCFbEPrimitiveTable(TStringList* SourceCsvStringList)
{
    this->FRowLabelList = new THashedStringList();
    this->FColumnLabelList = new THashedStringList();
    this->RowList = new TObjectList();

    // 空のデータセット
    if (SourceCsvStringList->Count == 0) {
        return;
    }

    // Column ラベルの取得
    TStringList* Delimited = new TStringList();
    this->CsvToStrings2(SourceCsvStringList->Strings[0].c_str(), Delimited);
    for (int j = 1; j < Delimited->Count; j++) {
        TCFbEPrimitiveTable::AddColumn(Delimited->Strings[j].c_str());
    }

    // Row の追加
    for (int i = 1; i < SourceCsvStringList->Count; i++) {
        Delimited->Clear();
        this->CsvToStrings2(SourceCsvStringList->Strings[i].c_str(), Delimited);
        if (Delimited->Count == 0) {
            continue;
        }

        int AddedRowIndex = TCFbEPrimitiveTable::AddRow(Delimited->Strings[0].c_str());

        for (int j = 1; j < Delimited->Count; j++) {
            if (Delimited->Strings[j].IsEmpty()) {
                continue;
            }
            
            try {
                this->GetCellByIndex(AddedRowIndex, j-1)->SetValue(Delimited->Strings[j].ToDouble());
            } catch (EConvertError &e) {
                // 型変換できない
            }
        }
    }

    delete Delimited;
}

//---------------------------------------------------------------------------
// デストラクタ
__fastcall TCFbEPrimitiveTable::~TCFbEPrimitiveTable()
{
    delete this->RowList;
    delete this->FColumnLabelList;
    delete this->FRowLabelList;
}

//---------------------------------------------------------------------------
// 列を削除する．
void __fastcall TCFbEPrimitiveTable::DeleteColumnByIndex(int TargetColumnIndex)
{
    this->FColumnLabelList->Delete(TargetColumnIndex);
    for (int i = 0; i < this->GetNumberOfRows() ; i++) {
        ((TObjectList*)this->RowList->Items[i])->Delete(TargetColumnIndex);
    }
}

//---------------------------------------------------------------------------
// 行を追加する．
// 行が既に存在する場合は例外を送出する．
// 追加した行の添字を返す
int __fastcall TCFbEPrimitiveTable::AddRow(const char* RowLabel)
{
    if (this->FRowLabelList->IndexOf(RowLabel) >= 0) {
        throw Exception("The row named \"" + AnsiString(RowLabel) + "\" was already existing.");
    }

    TObjectList* AddedRow = new TObjectList();
    for (int j = 0; j < this->GetNumberOfColumns() ; j++) {
        AddedRow->Add(new TCFbECell());
    }
    this->RowList->Add(AddedRow);

    return this->FRowLabelList->Add(RowLabel);
}

//---------------------------------------------------------------------------
// 列を追加する．
// 列が既に存在する場合は例外を送出する．
// 追加した列の添字を返す．
int __fastcall TCFbEPrimitiveTable::AddColumn(const char* ColumnLabel)
{
    if (this->FColumnLabelList->IndexOf(ColumnLabel) >= 0) {
        throw Exception("The column named \"" + AnsiString(ColumnLabel) + "\" was already existing.");
    }

    for (int i = 0; i < this->GetNumberOfRows() ; i++) {
        ((TObjectList*)this->RowList->Items[i])->Add(new TCFbECell());
    }

    return this->FColumnLabelList->Add(ColumnLabel);
}                                   

//---------------------------------------------------------------------------
// SourceTable の各要素をコピーする
void __fastcall TCFbEPrimitiveTable::Assign(TCFbEPrimitiveTable* SourceTable)
{
    // SourceTable の列要素をコピー
    for (int j = 0; j < SourceTable->GetNumberOfColumns(); j++) {
        if (this->GetNumberOfColumns() <= j) {
            this->AddColumn(SourceTable->FColumnLabelList->Strings[j].c_str());
        } else {
            this->FColumnLabelList->Strings[j] = SourceTable->FColumnLabelList->Strings[j];
        }
    }

    // 余分な列要素を削除
    while (this->GetNumberOfColumns() > SourceTable->GetNumberOfColumns()) {
        this->DeleteColumn(this->FColumnLabelList->Strings[this->GetNumberOfColumns() - 1].c_str());
    }

    // SourceTable の行要素をコピー
    for (int i = 0; i < SourceTable->GetNumberOfRows(); i++) {
        if (this->GetNumberOfRows() <= i) {
            this->AddRow(SourceTable->FRowLabelList->Strings[i].c_str());
        } else {
            this->FRowLabelList->Strings[i] = SourceTable->FRowLabelList->Strings[i];
        }
    }

    // 余分な行要素を削除
    while (this->GetNumberOfRows() > SourceTable->GetNumberOfRows()) {
        this->DeleteRow(this->FRowLabelList->Strings[this->GetNumberOfRows() - 1].c_str());
    }

    // 行列要素の値を全てコピー
    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        for (int j = 0; j < this->GetNumberOfColumns(); j++) {
            this->GetCellByIndex(i, j)->Assign(SourceTable->GetCellByIndex(i, j));
        }
    }
}

//---------------------------------------------------------------------------
// SourceTable の各要素の行と列を入替えてコピーする
void __fastcall TCFbEPrimitiveTable::RotatedAssign(TCFbEPrimitiveTable* SourceTable) {
    // SourceTable の行要素を列要素としてコピー
    for (int i = 0; i < SourceTable->GetNumberOfRows(); i++) {
        if (this->GetNumberOfColumns() <= i) {
            this->AddColumn(SourceTable->FRowLabelList->Strings[i].c_str());
        } else {
            this->FColumnLabelList->Strings[i] = SourceTable->FRowLabelList->Strings[i];
        }
    }

    // 余分な列要素を削除
    while (this->GetNumberOfColumns() > SourceTable->GetNumberOfRows()) {
        this->DeleteColumn(this->FColumnLabelList->Strings[this->GetNumberOfColumns() - 1].c_str());
    }

    // SourceTable の列要素を行要素としてコピー
    for (int j = 0; j < SourceTable->GetNumberOfColumns(); j++) {
        if (this->GetNumberOfRows() <= j) {
            this->AddRow(SourceTable->FColumnLabelList->Strings[j].c_str());
        } else {
            this->FRowLabelList->Strings[j] = SourceTable->FColumnLabelList->Strings[j];
        }

        for (int i = 0; i < this->GetNumberOfColumns(); i++) {
            this->GetCellByIndex(j, i)->Assign(SourceTable->GetCellByIndex(i, j));
        }
    }

    // 余分な列要素を削除
    while (this->GetNumberOfRows() > SourceTable->GetNumberOfColumns()) {
        this->DeleteRow(this->FRowLabelList->Strings[this->GetNumberOfRows() - 1].c_str());
    }
}

//---------------------------------------------------------------------------
// SourceTable の RowIndex 行目，ColumnIndex 列目のセルをコピーする
void __fastcall TCFbEPrimitiveTable::CopyCellFrom(TCFbEPrimitiveTable* SrcTable, int SrcRowIndex, int SrcColumnIndex, int DestRowIndex, int DestColumnIndex)
{
    TCFbECell* SrcCell = SrcTable->GetCellByIndex(SrcRowIndex, SrcColumnIndex);
    if (SrcCell->GetEnabled()) {
        this->SetValueByIndex(DestRowIndex, DestColumnIndex, SrcCell->GetValue());
    } else {
        this->UnableElementByIndex(DestRowIndex, DestColumnIndex);
    }
}

//---------------------------------------------------------------------------
// SourceTable の SourceRowIndex 行目をコピーする
int __fastcall TCFbEPrimitiveTable::CopyRowFrom(TCFbEPrimitiveTable* SourceTable, int SourceRowIndex, bool AddColumn)
{
    int UpdatedIndex = this->FRowLabelList->IndexOf(SourceTable->FRowLabelList->Strings[SourceRowIndex]);
    if (UpdatedIndex < 0) {    // 行が存在しない
        UpdatedIndex = this->AddRow(SourceTable->FRowLabelList->Strings[SourceRowIndex].c_str());
    }
    for (int j = 0; j < SourceTable->GetNumberOfColumns(); j++) {
        int    TargetColumn = this->FColumnLabelList->IndexOf(SourceTable->FColumnLabelList->Strings[j]);
        if (TargetColumn < 0) {
            if (!AddColumn) {   // 列を追加しない
                continue;
            }
            TargetColumn = this->AddColumn(SourceTable->FColumnLabelList->Strings[j].c_str());
        }
        TCFbECell*  SourceCell = SourceTable->GetCellByIndex(SourceRowIndex, j);
        if (SourceCell->GetEnabled()) {
            this->SetValueByIndex(UpdatedIndex, TargetColumn, SourceCell->GetValue());
        } else {
            this->UnableElementByIndex(UpdatedIndex, TargetColumn);
        }
    }

    return UpdatedIndex;
}

//---------------------------------------------------------------------------
// SourceTable の SourceRowIndex 行目を列としてコピーする
int __fastcall TCFbEPrimitiveTable::CopyRowAsColumnFrom(TCFbEPrimitiveTable* SourceTable, int SourceRowIndex, bool AddRow)
{
    int UpdatedIndex = this->FColumnLabelList->IndexOf(SourceTable->FRowLabelList->Strings[SourceRowIndex]);
    if (UpdatedIndex < 0) {    // 行が存在しない
        UpdatedIndex = this->AddColumn(SourceTable->FRowLabelList->Strings[SourceRowIndex].c_str());
    }
    for (int j = 0; j < SourceTable->GetNumberOfColumns(); j++) {
        int    TargetRow = this->FRowLabelList->IndexOf(SourceTable->FColumnLabelList->Strings[j]);
        if (TargetRow < 0) {
            if (!AddRow) {   // 列を追加しない
                continue;
            }
            TargetRow = this->AddRow(SourceTable->FColumnLabelList->Strings[j].c_str());
        }
        TCFbECell*  SourceCell = SourceTable->GetCellByIndex(SourceRowIndex, j);
        if (SourceCell->GetEnabled()) {
            this->SetValueByIndex(TargetRow, UpdatedIndex, SourceCell->GetValue());
        } else {
            this->UnableElementByIndex(TargetRow, UpdatedIndex);
        }
    }

    return UpdatedIndex;
}

//---------------------------------------------------------------------------
// SourceTable の SourceColumnIndex 列目をコピーする
int __fastcall TCFbEPrimitiveTable::CopyColumnFrom(TCFbEPrimitiveTable* SourceTable, int SourceColumnIndex, bool AddRow)
{
    int UpdatedIndex = this->FColumnLabelList->IndexOf(SourceTable->FColumnLabelList->Strings[SourceColumnIndex]);
    if (UpdatedIndex < 0) {    // 列目が存在しない
        UpdatedIndex = this->AddColumn(SourceTable->FColumnLabelList->Strings[SourceColumnIndex].c_str());
    }
    for (int i = 0; i < SourceTable->GetNumberOfRows(); i++) {
        int    TargetRow = this->FRowLabelList->IndexOf(SourceTable->FRowLabelList->Strings[i]);
        if (TargetRow < 0) {
            if (!AddRow) {   // 列を追加しない
                continue;
            }
            TargetRow = this->AddRow(SourceTable->FRowLabelList->Strings[i].c_str());
        }
        TCFbECell*  SourceCell = SourceTable->GetCellByIndex(i, SourceColumnIndex);
        if (SourceCell->GetEnabled()) {
            this->SetValueByIndex(TargetRow, UpdatedIndex, SourceCell->GetValue());
        } else {
            this->UnableElementByIndex(TargetRow, UpdatedIndex);
        }
    }

    return UpdatedIndex;
}

//---------------------------------------------------------------------------
// SourceTable の SourceColumnIndex 列目を列としてコピーする
int __fastcall TCFbEPrimitiveTable::CopyColumnAsRowFrom(TCFbEPrimitiveTable* SourceTable, int SourceColumnIndex, bool AddColumn)
{
    int UpdatedIndex = this->FRowLabelList->IndexOf(SourceTable->FColumnLabelList->Strings[SourceColumnIndex]);
    if (UpdatedIndex < 0) {    // 列目が存在しない
        UpdatedIndex = this->AddRow(SourceTable->FColumnLabelList->Strings[SourceColumnIndex].c_str());
    }
    for (int i = 0; i < SourceTable->GetNumberOfRows(); i++) {
        int    TargetColumn = this->FColumnLabelList->IndexOf(SourceTable->FRowLabelList->Strings[i]);
        if (TargetColumn < 0) {
            if (!AddColumn) {   // 列を追加しない
                continue;
            }
            TargetColumn = this->AddColumn(SourceTable->FRowLabelList->Strings[i].c_str());
        }
        TCFbECell*  SourceCell = SourceTable->GetCellByIndex(i, SourceColumnIndex);
        if (SourceCell->GetEnabled()) {
            this->SetValueByIndex(UpdatedIndex, TargetColumn, SourceCell->GetValue());
        } else {
            this->UnableElementByIndex(UpdatedIndex, TargetColumn);
        }
    }

    return UpdatedIndex;
}

//---------------------------------------------------------------------------
// 列で Enabled なセルの数を返す
int    __fastcall TCFbEPrimitiveTable::GetEnabledCountOfRow(int i)
{
    int    Count = 0;
    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        if (((TCFbECell*)this->GetCellByIndex(i, j))->GetEnabled()) {
            Count++;
        }
    }
    return    Count;
}

//---------------------------------------------------------------------------
// 列で Enabled なセルの数を返す
int    __fastcall TCFbEPrimitiveTable::GetEnabledCountOfColumn(int j)
{
    int    Count = 0;
    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        if (this->GetEnabledByIndex(i, j)) {
//        if (((TCFbECell*)this->GetCellByIndex(i, j))->GetEnabled()) {
            Count++;
        }
    }
    return    Count;
}

//---------------------------------------------------------------------------
// TargetRowIndex 行目と ComparedRowIndex 行目で共通して Enabled な要素をソートし，TargetSortedCellList と　ComparedSortedCellList にそれぞれ入れて返す（RankCorrelation で使われる
void __fastcall TCFbEPrimitiveTable::GetBothEnableSortedCellList(int TargetRowIndex, int ComparedRowIndex, TList* TargetSortedCellList, TList* ComparedSortedCellList)
{
    TargetSortedCellList->Clear();
    ComparedSortedCellList->Clear();
    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        TCFbECell* TargetCell = this->GetCellByIndex(TargetRowIndex, j);
        TCFbECell* ComparedCell = this->GetCellByIndex(ComparedRowIndex, j);
        if (TargetCell->GetEnabled() && ComparedCell->GetEnabled()) {
            TargetSortedCellList->Add(TargetCell);
            ComparedSortedCellList->Add(ComparedCell);
        }
    }
    TargetSortedCellList->Sort(CompareCell);
    ComparedSortedCellList->Sort(CompareCell);
}

//---------------------------------------------------------------------------
// RowIndex 番目の行の Enabled な要素を DynArray に入れて返す
void __fastcall TCFbEPrimitiveTable::GetEnabledCellsInRow(int RowIndex, TDoubleDynArray& DynArray)
{
    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        TCFbECell* CurrentCell = this->GetCellByIndex(RowIndex, j);
        if (CurrentCell->GetEnabled()) {
            DynArray.Length++;
            DynArray[DynArray.High] = CurrentCell->GetValue();
        }
    }
}

//---------------------------------------------------------------------------
// ColumnIndex 番目の列の Enabled な要素を DynArray に入れて返す
void __fastcall TCFbEPrimitiveTable::GetEnabledCellsInColumn(int ColumnIndex, TDoubleDynArray& DynArray)
{
    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        TCFbECell* CurrentCell = this->GetCellByIndex(i, ColumnIndex);
        if (CurrentCell->GetEnabled()) {
            DynArray.Length++;
            DynArray[DynArray.High] = CurrentCell->GetValue();
        }
    }
}

//---------------------------------------------------------------------------
// 以下，protected method
//---------------------------------------------------------------------------
// RowIndex 番目の列の Enabled な要素をソートし，SortedCellList に入れて返す
void __fastcall TCFbEPrimitiveTable::GetSortedCellListOfRow(int RowIndex, TList* SortedCellList)
{
    SortedCellList->Clear();
    for (int j = 0; j < this->GetNumberOfColumns(); j++) {
        TCFbECell* CurrentCell = this->GetCellByIndex(RowIndex, j);
        if (CurrentCell->GetEnabled()) {
            SortedCellList->Add(CurrentCell);
        }
    }
    SortedCellList->Sort(CompareCell);
}

//---------------------------------------------------------------------------
// ColumnIndex 番目の列の Enabled な要素をソートし，SortedCellList に入れて返す
void __fastcall TCFbEPrimitiveTable::GetSortedCellListOfColumn(int ColumnIndex, TList* SortedCellList)
{
    SortedCellList->Clear();
    for (int i = 0; i < this->GetNumberOfRows(); i++) {
        TCFbECell* CurrentCell = this->GetCellByIndex(i, ColumnIndex);
        if (CurrentCell->GetEnabled()) {
            SortedCellList->Add(CurrentCell);
        }
    }
    SortedCellList->Sort(CompareCell);
}

//---------------------------------------------------------------------------
void __fastcall TCFbEPrimitiveTable::UnableAllElements(void)
{
    for (int i = 0; i < this->FRowLabelList->Count; i++) {
        for (int j = 0; j < this->FColumnLabelList->Count; j++) {
            this->UnableElementByIndex(i, j);
        }
    }
}

//---------------------------------------------------------------------------
// CSV形式の文字列をTStringsに分解する
// このメソッドを高速化すれば，CSV データの読み込みを早くできる
// TStrings を使わない実装が考えられないか（TStrings は遅い）
void __fastcall TCFbEPrimitiveTable::CsvToStrings2(char* str, TStrings *List)
{
    AnsiString    Buffer = "";
    char    tmp[BUFFERLEN];
    bool    quote = false;     // 引用中か

    int    strIndex = 0;
    int    tmpIndex = 0;

    while (str[strIndex] != '\0') {
        if((str[strIndex] == '\"') || (str[strIndex] == '\'')){    //引用符
            quote =!quote;
        } else if(!quote && (str[strIndex] == ',')) {    //区切り文字
            tmp[tmpIndex] = '\0';
            Buffer += AnsiString(tmp);
            List->Add(Buffer);
            Buffer = "";
            tmpIndex = 0;
        } else if(!quote && isspace(str[strIndex])) {    //空白(必要かな？)
            ;
        } else {    //その他
            tmp[tmpIndex++] = str[strIndex];
            if (tmpIndex + 1 > BUFFERLEN) {
                tmp[tmpIndex] = '\0';
                Buffer += AnsiString(tmp);
            }
        }
        strIndex++;
    }

    tmp[tmpIndex] = '\0';
    Buffer += AnsiString(tmp);
    List->Add(Buffer);
}

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

