//---------------------------------------------------------------------------
#pragma hdrstop

#include "TCommandLineParser.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
// コンストラクタ（argc と argv によってコマンドラインを与える
__fastcall TCommandLineParser::TCommandLineParser(int argc, char* argv[])
{
    this->FOptionList = new TStringList();

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


    // -h, --help パラメータの検査
    if (this->IsEmpty() || this->IsEnableOption("-h", "--help")) {
        fprintf(stderr, "%s", helpStr.c_str());
        delete this->FOptionList;
        Abort();    // サイレント例外で終了させる
    }

    this->TargetColumnLabelList = new TStringList;

    // -l, --learning パラメータの検査
    this->LearningDataFileName = this->GetValueString("-l", "--learning");
    if (this->LearningDataFileName.IsEmpty()) {
        throw EDocfbeError("No learning data file name.", 1);
    }
    if (!FileExists(this->LearningDataFileName)) {
        throw EDocfbeError("Learning data file \"" + LearningDataFileName + "\" is not found.", 2);
    }

    // -e, --estimating パラメータの検査
    this->EstimatingDataFileName = this->GetValueString("-e", "--estimating");
    if (this->EstimatingDataFileName.IsEmpty()) {
        throw EDocfbeError("No estimating data file name.", 3);
    } else if (!FileExists(this->EstimatingDataFileName)) {
        throw EDocfbeError("Estimating data file \"" + EstimatingDataFileName + "\" is not found.", -1);
    }

    /////
    // -s, --similarity パラメータの検査
    this->SimilarityComputationName = this->GetValueString("-s", --similarity");
    if (this->SimilarityComputationName.IsEmpty()) {
        throw EDocfbeError("No similarity computation algorithm.", 4);
    } else {
        TCFbESimilarityComputationAlgorithm* SCAlgorithm = GetSimilarityComputationAlgorithm(this->SimilarityComputationName);
        if (SCAlgorithm == NULL) {
            throw EDocfbeError("Similarity computation algorithm \"" + SimilarityComputationName + "\" is illegal.", -2);
        } else {
            delete SCAlgorithm;
        }
    }

    // -p, --prediction パラメータの検査
    PredictionName = this->GetValueString("-p", "--prediction");
    if (PredictionName.IsEmpty()) {
        throw EDocfbeError("No prediction algorithm.", 5);
    } else {
        TCFbEPredictionAlgorithm* PAlgorithm = GetPredictionAlgorithm(PredictionName, 1);
        if (PAlgorithm == NULL) {
            throw EDocfbeError("Prediction algorithm \"" + PredictionName + "\" is illegal.", -3);
        } else {
            delete PAlgorithm;
        }
    }

    // -n, --normalize パラメータの検査
    AnsiString NormalizeMethod = this->GetValueString("-n", "--normalize");
    if (NormalizeMethod.IsEmpty() || NormalizeMethod.AnsiCompareIC("value")) {
        ValueBase = vbValue;
    } else if (NormalizeMethod.IsEmpty() || (NormalizeMethod.AnsiCompareIC("standardize") == 0)) {
        ValueBase = vbStandardizedValue;
    } else if (NormalizeMethod.AnsiCompareIC("normalize") == 0) {
        ValueBase = vbNormzlizedValue;
    } else if (NormalizeMethod.AnsiCompareIC("order") == 0) {
        ValueBase = vbOrder;
    } else {
        throw EDocfbeError("Normalization method \"" + NormalizeMethod + "\" illegal.", -4);
    }

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

    // -ns,--neighbors-size パラメータの検査
    AnsiString NSString = this->GetValueString("-ns", "--neighbors-size");
    if (NSString.IsEmpty()) {
        NeighborsSize = 0;
    } else {
        try {
            NeighborsSize = NSString;
        } catch (EConvertError &ece) {
            throw EDocfbeError("Neighbors size \"" + NSString + "\" is illegal.", -5);
        }
    }

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

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

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

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

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

//---------------------------------------------------------------------------
__fastcall TCommandLineParser::~TCommandLineParser()
{
    if (this->PredictionAlgorithm != NULL) {
        delete this->PredictionAlgorithm;
    } else if (this->SimilarityComputationAlgorithm != NULL) {
        delete this->SimilarityComputationAlgorithm;
    }
    delete this->TargetColumnLabelList;
    delete this->FOptionList;
}

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

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

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

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

//---------------------------------------------------------------------------
// 文字列を与えて，対応する類似度計算アルゴリズムのインスタンスを返す
TCFbESimilarityComputationAlgorithm* __fastcall TCommandLineParser::GetSimilarityComputationAlgorithm(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 TCommandLineParser::GetPredictionAlgorithm(AnsiString AlgorithmName, int NeighborsSize)
{
    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;    // 不正なアルゴリズム名
}

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

