#include <hdf/HDFCmpExperimentGroup.hpp>

#include <iostream>

bool HDFCmpExperimentGroup::Create(HDFGroup &parent, std::string experimentGroupName)
{

    parent.AddGroup(experimentGroupName);
    if (experimentGroup.Initialize(parent.group, experimentGroupName) == 0) {
        return 0;
    }
    alignmentArray.Create(experimentGroup, "AlnArray");
    return true;
}

void HDFCmpExperimentGroup::AddAlignment(std::vector<unsigned char> &alignment,
                                         unsigned int &offsetBegin, unsigned int &offsetEnd)
{

    offsetBegin = offsetEnd = 0;
    if (alignment.size() == 0) {
        //
        // Do not increment anything.
        //
        return;
    }

    // Make a copy of alignment.
    std::vector<unsigned char> paddedAlignment = alignment;
    // Pad '0' to the end of the alignment.
    paddedAlignment.push_back(0);

    offsetBegin = alignmentArray.size();         // 0 based, inclusive
    offsetEnd = offsetBegin + alignment.size();  // 0 based, exclusive
    // alignmentArray[offsetEnd] is not a part of the real alignment, it is the padded 0.
    alignmentArray.Write(&paddedAlignment[0], paddedAlignment.size());
}

//template<typename T>
//void HDFCmpExperimentGroup::AddQualityValues(const std::vector<T> &qualityValues,
//                                             const std::string &fieldName,
//                                             unsigned int *offsetBegin,
//                                             unsigned int *offsetEnd) {
//
//  std::vector<T> paddedQualityValues = qualityValues;
//  paddedQualityValues.push_back(0);
//  HDFArray<T> *arrayPtr = NULL;
//
//  // This seems to be how we do it
//  if (fieldName == "DeletionQV") {
//      arrayPtr = &deletionQV;
//  } else if (fieldName == "InsertionQV") {
//      arrayPtr = &insertionQV;
//  } else if (fieldName == "MergeQV") {
//      arrayPtr = &mergeQV;
//  } else if (fieldName == "SubstitutionQV") {
//      arrayPtr = &substitutionQV;
//  } else if (fieldName == "DeletionTag") {
//      arrayPtr = &deletionTag;
//  } else if (fieldName == "SubstitutionTag") {
//      arrayPtr = &substitutionTag;
//  } else {
//      assert(false);
//  }
//
//  *offsetBegin = arrayPtr->size();
//  *offsetEnd = arrayPtr->size() + qualityValues.size();
//
//  arrayPtr->Write(&paddedQualityValues[0], paddedQualityValues.size());
//}
//
//// One for tags, one for QVs
//template void HDFCmpExperimentGroup::AddQualityValues<char>(
//        const std::vector<char>&, const std::string&,
//        unsigned int*, unsigned int*);
//
//template void HDFCmpExperimentGroup::AddQualityValues<UChar>(
//        const std::vector<UChar>&, const std::string&,
//        unsigned int*, unsigned int*);
//
int HDFCmpExperimentGroup::Initialize(HDFGroup &refGroup, std::string experimentGroupName,
                                      std::set<std::string> &fieldNames)
{
    //
    // Normal initialization that prepares for reading alignments
    //
    Initialize(refGroup, experimentGroupName);

    //
    // Field initialization for reading in pulse/quality information.
    //
    std::set<std::string>::iterator fieldNameIt, fieldEnd;
    fieldEnd = fieldNames.end();
    for (fieldNameIt = fieldNames.begin(); fieldNameIt != fieldEnd; ++fieldNameIt) {
        if (supportedFields.find(*fieldNameIt) != supportedFields.end() and
            experimentGroup.ContainsObject(*fieldNameIt)) {
            fields[*fieldNameIt]->Initialize(experimentGroup, *fieldNameIt);
        } else {
            std::cout << "Unable to initialize requested field " << *fieldNameIt
                      << " in experiment group " << experimentGroupName << std::endl;
        }
    }
    return 1;
}

int HDFCmpExperimentGroup::Initialize(HDFGroup &refGroup, std::string experimentGroupName)
{

    if (experimentGroup.Initialize(refGroup.group, experimentGroupName) == 0) {
        return 0;
    }
    if (alignmentArray.Initialize(experimentGroup, "AlnArray") == 0) {
        return 0;
    }
    return 1;
}

HDFCmpExperimentGroup::HDFCmpExperimentGroup()
{
    fields["StartTimeOffset"] = &this->startTimeOffset;
    fields["QualityValue"] = &this->qualityValue;
    fields["IPD"] = &this->ipd;
    fields["PreBaseFrames"] = &this->preBaseFrames;
    fields["DeletionQV"] = &this->deletionQV;
    fields["InsertionQV"] = &this->insertionQV;
    fields["ClassifierQV"] = &this->classifierQV;
    fields["SubstitutionQV"] = &this->substitutionQV;
    fields["Light"] = &this->light;
    fields["WidthInFrames"] = &this->widthInFrames;
    fields["PulseWidth"] = &this->pulseWidth;
    fields["StartFrame"] = &this->startTime;
    // StartFrame computed from Pulse metrics and Base metrics
    fields["StartFramePulse"] = &this->startTimePulse;
    fields["StartFrameBase"] = &this->startTimeBase;

    fields["pkmid"] = &this->pkmid;
    fields["pkmax"] = &this->pkmax;
    fields["pkmean"] = &this->pkmean;
    fields["DeletionTag"] = &this->deletionTag;
    fields["SubstitutionTag"] = &this->substitutionTag;
    fields["PulseIndex"] = &this->pulseIndex;
    fields["MergeQV"] = &this->mergeQV;
}

// Return reference alignment AlnArray size in KB.
UInt HDFCmpExperimentGroup::GetAlnArraySize()
{
    return alignmentArray.arrayLength / 1024 * sizeof(unsigned char);
}

void HDFCmpExperimentGroup::AddQVs(const std::vector<UChar> &qualityValues,
                                   const std::string &fieldName, unsigned int *offsetBegin,
                                   unsigned int *offsetEnd)
{
    std::vector<UChar> paddedQualityValues = qualityValues;
    paddedQualityValues.push_back(0);
    HDFArray<UChar> *arrayPtr = NULL;

    // This seems to be how we do it
    if (fieldName == "DeletionQV") {
        arrayPtr = &deletionQV;
    } else if (fieldName == "InsertionQV") {
        arrayPtr = &insertionQV;
    } else if (fieldName == "MergeQV") {
        arrayPtr = &mergeQV;
    } else if (fieldName == "SubstitutionQV") {
        arrayPtr = &substitutionQV;
    } else {
        assert(false);
    }

    if (!arrayPtr->isInitialized) arrayPtr->Initialize(experimentGroup, fieldName);
    *offsetBegin = arrayPtr->size();
    *offsetEnd = arrayPtr->size() + qualityValues.size();

    arrayPtr->Write(&paddedQualityValues[0], paddedQualityValues.size());
}

void HDFCmpExperimentGroup::AddTags(const std::vector<char> &qualityValues,
                                    const std::string &fieldName, unsigned int *offsetBegin,
                                    unsigned int *offsetEnd)
{
    std::vector<char> paddedQualityValues = qualityValues;
    paddedQualityValues.push_back(0);
    HDFArray<char> *arrayPtr = NULL;

    if (fieldName == "DeletionTag") {
        arrayPtr = &deletionTag;
    } else if (fieldName == "SubstitutionTag") {
        arrayPtr = &substitutionTag;
    } else {
        assert(false);
    }

    if (!arrayPtr->isInitialized) arrayPtr->Initialize(experimentGroup, fieldName);
    *offsetBegin = arrayPtr->size();
    *offsetEnd = arrayPtr->size() + qualityValues.size();

    arrayPtr->Write(&paddedQualityValues[0], paddedQualityValues.size());
}
