/*
    TCFbECommandLineParser.cpp    June 6, 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 "TCFbECommandLineParser.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
// コンストラクタ（argc と argv によってコマンドラインを与える
__fastcall TCFbECommandLineParser::TCFbECommandLineParser(int argc, char* argv[])
{
    // コマンドライン文字数が 1024 文字以上
    if (StrLen(CmdLine) >= 1024) {
        this->ErrorCode = 6;
        return;
    }

    this->LearningDataFileNameList = new TStringList();
    this->EstimatingDataFileNameList = new TStringList();    
    this->FOptionList = new TStringList();
    this->TargetColumnLabelList = new TStringList;

    if (argc > 0) {
        for (int i = 0; i < argc; i++) {
            this->FOptionList->Add(AnsiString(argv[i]));
        }
    } else {
        for (int i = 0; i < ParamCount(); i++) {
            this->FOptionList->Add(ParamStr(i));
        }
    }

    try {
        // -h, --help パラメータの検査
        if (this->IsEmpty() || this->IsEnableOption("-h", "--help")) {
            this->HelpEnabled = true;
            return;
        }
        this->HelpEnabled = false;

        // -l, --learning パラメータの検査
        this->LearningDataFileNameList->DelimitedText = this->GetValueString("-l", "--learning");
        if (this->LearningDataFileNameList->Count <= 0) {
            this->ErrorCode = 1;
            return;
        }
        for (int k = 0; k < this->LearningDataFileNameList->Count; k++) {
            if (!FileExists(this->LearningDataFileNameList->Strings[k])) {
                this->ErrorCode = 2;
                this->ErrorParameter = this->LearningDataFileNameList->Strings[k];
                return;
            }
        }

        // -e, --estimating パラメータの検査
        this->EstimatingDataFileNameList->DelimitedText = this->GetValueString("-e", "--estimating");
        if (this->EstimatingDataFileNameList->Count <= 0) {
            this->ErrorCode = 3;
            return;
        }
        for (int k = 0; k < this->EstimatingDataFileNameList->Count; k++) {
            if (!FileExists(this->EstimatingDataFileNameList->Strings[k])) {
                this->ErrorCode = -1;
                this->ErrorParameter = this->EstimatingDataFileNameList->Strings[k];
                return;
            }
        }

        // -s, --similarity パラメータの検査
        this->SimilarityComputationName = this->GetValueString("-s", "--similarity");
        if (this->SimilarityComputationName.IsEmpty()) {
            this->ErrorCode = 4;
            return;
        } else {
            TCFbESimilarityComputationAlgorithm*    SCA = this->CreateSimilarityComputationAlgorithm(this->SimilarityComputationName);
            if (SCA == NULL) {
                this->ErrorCode = -2;
                this->ErrorParameter = this->SimilarityComputationName;
                return;
            }
            delete SCA;
        }

        // -p, --prediction パラメータの検査
        this->PredictionName = this->GetValueString("-p", "--prediction");
        if (this->PredictionName.IsEmpty()) {
            this->ErrorCode = 5;
            return;
        } else {
            TCFbEPredictionAlgorithm* PA = CreatePredictionAlgorithm(this->PredictionName);
            if (PA == NULL) {
                this->ErrorCode = -3;
                this->ErrorParameter = this->PredictionName;
                return;
            }
            delete PA;
        }

        // -n, --normalize パラメータの検査
        AnsiString NormalizeMethod = this->GetValueString("-n", "--normalize");
        if (NormalizeMethod.IsEmpty() || (NormalizeMethod.AnsiCompareIC("value") == 0)) {
            this->ValueBase = vbValue;
        } else if (NormalizeMethod.AnsiCompareIC("standardize") == 0) {
            this->ValueBase = vbStandardizedValue;
        } else if (NormalizeMethod.AnsiCompareIC("normalize") == 0) {
            this->ValueBase = vbNormalizedValue;
        } else if (NormalizeMethod.AnsiCompareIC("order") == 0) {
            this->ValueBase = vbOrder;
        } else if (NormalizeMethod.AnsiCompareIC("d-standardize") == 0) {
            this->ValueBase = vbDoubleStandardizedValue;
        } else if (NormalizeMethod.AnsiCompareIC("d-normalize") == 0) {
            this->ValueBase = vbDoubleNormalizedValue;
        } else if (NormalizeMethod.AnsiCompareIC("d-order") == 0) {
            this->ValueBase = vbDoubleOrder;
        } else {
            this->ErrorCode = -4;
            this->ErrorParameter = NormalizeMethod;
            return;
        }

        // -i, --itembased パラメータの検査
        if (this->IsEnableOption("-i", "--itembased")) {
            this->AlgorithmBase = abColumn;
        } else {
            this->AlgorithmBase = abRow;
        }

        // -ns,--neighbors-size パラメータの検査
        AnsiString NSString = this->GetValueString("-ns", "--neighbors-size");
        if (NSString.IsEmpty()) {
            this->NeighborsSize = 0;
        } else {
            try {
                this->NeighborsSize = NSString.ToInt();
            } catch (EConvertError &ece) {
                this->ErrorCode = -5;
                this->ErrorParameter = NSString;
                return;
            }
        }

        // -t, --target パラメータの検査
        this->TargetColumnLabelList->DelimitedText = this->GetValueString("-t", "--target");

        // -d,--default パラメータの検査
        AnsiString DefaultString = this->GetValueString("-d", "--default");
        if (DefaultString.IsEmpty()) {
            this->DefaultValueEnabled = false;
            this->DefaultValue = 0.0;
        } else {
            try {
                this->DefaultValueEnabled = true;
                this->DefaultValue = DefaultString.ToDouble();
            } catch (EConvertError &ece) {
                this->ErrorCode = -14;
                this->ErrorParameter = DefaultString;
                return;
            }
        }

        // -if, --inverse-frequency パラメータの検査
        this->InverseCaseFrequencyEnabled = this->IsEnableOption("-if", "--inverse-frequency");
                                                 
        // -ca,--case-amplifier パラメータの検査
        AnsiString CAString = this->GetValueString("-ca", "--case-amplifier");
        if (CAString.IsEmpty()) {
            this->CaseAmplifier = 1.0;
        } else {
            try {
                this->CaseAmplifier = CAString.ToDouble();
            } catch (EConvertError &ece) {
                this->ErrorCode = -16;
                this->ErrorParameter = CAString;
                return;
            }
        }

        // -m, --merge パラメータの検査
        AnsiString MergeBaseString = this->GetValueString("-m", "--merge");
        if (MergeBaseString.IsEmpty() || (MergeBaseString.AnsiCompareIC("both") == 0)) {
            this->MergeBase = tmbBoth;
        } else if (MergeBaseString.AnsiCompareIC("none") == 0) {
            this->MergeBase = tmbNone;
        } else if (MergeBaseString.AnsiCompareIC("learning") == 0) {
            this->MergeBase = tmbLearningData;
        } else if (MergeBaseString.AnsiCompareIC("estimating") == 0) {
            this->MergeBase = tmbEstimatingData;
        } else {
            this->ErrorCode = -15;
            this->ErrorParameter = MergeBaseString;
            return;
        }

        // -dn, --disp-neighbors パラメータの検査
        this->DisplayNeighbors = this->IsEnableOption("-dn", "--disp-neighbors");

        // -ds, --disp-similarities パラメータの検査
        this->DisplaySimilarities = this->IsEnableOption("-ds", "--disp-similarities");

        // -dv, --disp-values パラメータの検査
        this->DisplayValues = this->IsEnableOption("-dv", "--disp-values");

        // -dd, --disp-distribution パラメータの検査
        this->DisplayDistribution = this->IsEnableOption("-dd", "--disp-distribution");

        // -r, --recommendation パラメータの検査
        this->Recommendation = this->IsEnableOption("-r", "--recommendation");

        // -o,--output パラメータの検査
        this->OutputFileName = this->GetValueString("-o", "--output");
        if (!this->OutputFileName.IsEmpty()) {
            AnsiString OutputDirectory = ExtractFileDir(this->OutputFileName);
            if (!DirectoryExists(OutputDirectory)) {
                try {
                    MkDir(OutputDirectory);    // ディレクトリ作成
                } catch (...) {
                    this->ErrorCode = -6;
                    this->ErrorParameter = OutputDirectory;
                    return;
                }
            }
        }
    } catch (...) {
        delete this->FOptionList;
        delete this->TargetColumnLabelList;
        throw;
    }
}

//---------------------------------------------------------------------------
__fastcall TCFbECommandLineParser::~TCFbECommandLineParser()
{
    delete this->TargetColumnLabelList;
    delete this->FOptionList;
    delete this->LearningDataFileNameList;
    delete this->EstimatingDataFileNameList;
}

//---------------------------------------------------------------------------
// オプションがない
bool __fastcall TCFbECommandLineParser::IsEmpty(void)
{
    return (this->FOptionList->Count == 1);
}

//---------------------------------------------------------------------------
// OptionLabel0, OptionLabel1 のどちらかが OptionList に含まれていれば返す
bool __fastcall TCFbECommandLineParser::IsEnableOption(AnsiString OptionLabel0, AnsiString OptionLabel1)
{
    return (this->FOptionList->IndexOf(OptionLabel0) >= 0)
        || (this->FOptionList->IndexOf(OptionLabel1) >= 0);
}

//---------------------------------------------------------------------------
// OptionLabel0, OptionLabel1 のどちらかと OptionLabel=文字列 という形式で関連付けられた文字列があれば true を返す
bool __fastcall TCFbECommandLineParser::IsEnableValue(AnsiString OptionLabel0, AnsiString OptionLabel1)
{
    return !this->FOptionList->Values[OptionLabel0].IsEmpty() || !this->FOptionList->Values[OptionLabel1].IsEmpty();
}

//---------------------------------------------------------------------------
// OptionLabel0, OptionLabel1 のどちらかと OptionLabel=文字列 という形式で関連付けられた文字列が OptionList にあれば返す
AnsiString __fastcall TCFbECommandLineParser::GetValueString(AnsiString OptionLabel0, AnsiString OptionLabel1)
{
    AnsiString    Buffer = this->FOptionList->Values[OptionLabel0];
    if (Buffer.IsEmpty()) {
        Buffer = this->FOptionList->Values[OptionLabel1];
    }
    return Buffer;
}

//---------------------------------------------------------------------------
TCFbESimilarityComputationAlgorithm* __fastcall TCFbECommandLineParser::GetSimilarityComputationAlgorithm(void)
{
    return this->CreateSimilarityComputationAlgorithm(this->SimilarityComputationName);
}

//---------------------------------------------------------------------------
TCFbEPredictionAlgorithm* __fastcall TCFbECommandLineParser::GetPredictionAlgorithm(void)
{
    return this->CreatePredictionAlgorithm(this->PredictionName);
}

//---------------------------------------------------------------------------
// 文字列を与えて，対応する類似度計算アルゴリズムのインスタンスを返す
TCFbESimilarityComputationAlgorithm* __fastcall TCFbECommandLineParser::CreateSimilarityComputationAlgorithm(AnsiString AlgorithmName)
{
    if (AlgorithmName == "CosineSimilarity") {
        return new TCFbECosineSimilarity();

    } else if (AlgorithmName == "AdjustedCosineSimilarityWithAverage") {
        return new TCFbEAdjustedCosineSimilarityWithAverage();

    } else if (AlgorithmName == "AdjustedCosineSimilarityWithMedian") {
        return new TCFbEAdjustedCosineSimilarityWithMedian();

    } else if (AlgorithmName == "CorrelationCoefficientWithAverage") {
        return new TCFbECorrelationCoefficientWithAverage();

    } else if (AlgorithmName == "CorrelationCoefficientWithMedian") {
        return new TCFbECorrelationCoefficientWithMedian();

    } else if (AlgorithmName == "RankCorrelation") {
        return new TCFbERankCorrelation();

    } else if (AlgorithmName == "DistanceSimilarityWithAverage") {
        return new TCFbEDistanceSimilarityWithAverage();

    } else if (AlgorithmName == "DistanceSimilarityWithMedian") {
        return new TCFbEDistanceSimilarityWithMedian();
    }

    return NULL;    // 不正なアルゴリズム名
}

//---------------------------------------------------------------------------
// 文字列を与えて，対応する予測アルゴリズムのインスタンスを返す
TCFbEPredictionAlgorithm* __fastcall TCFbECommandLineParser::CreatePredictionAlgorithm(AnsiString AlgorithmName)
{
    if (AlgorithmName == "WeightedSum") {
        return new TCFbEWeightedSum();

    } else if (AlgorithmName == "AdjustedWeightedSumWithAverageOfColumn") {
        return new TCFbEAdjustedWeightedSumWithAverageOfColumn();

    } else if (AlgorithmName == "AdjustedWeightedSumWithMedianOfColumn") {
        return new TCFbEAdjustedWeightedSumWithMedianOfColumn();

    } else if (AlgorithmName == "AdjustedWeightedSumWithAverageOfNeighbors") {
        return new TCFbEAdjustedWeightedSumWithAverageOfNeighbors();

    } else if (AlgorithmName == "AdjustedWeightedSumWithMedianOfNeighbors") {
        return new TCFbEAdjustedWeightedSumWithMedianOfNeighbors();

    } else if (AlgorithmName == "AdjustedWeightedSumWithAverageOfRow") {
        return new TCFbEAdjustedWeightedSumWithAverageOfRow();

    } else if (AlgorithmName == "AdjustedWeightedSumWithMedianOfRow") {
        return new TCFbEAdjustedWeightedSumWithMedianOfRow();

    } else if (AlgorithmName == "AmplifiedWeightedSumWithAveragedMultiplier") {
        return new TCFbEAmplifiedWeightedSumWithAveragedMultiplier();

    } else if (AlgorithmName == "AmplifiedWeightedSumWithMedianOfMultiplier") {
        return new TCFbEAmplifiedWeightedSumWithMedianOfMultiplier();

    } else if (AlgorithmName == "AmplifiedWeightedSumWithWeightedMultiplier") {
        return new TCFbEAmplifiedWeightedSumWithWeightedMultiplier();
    }

    return NULL;    // 不正なアルゴリズム名
}

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

