// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2013, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================
// Author: Rene Rahn <rene.rahn@fu-berlin.de>
// ==========================================================================

#include <string>
#include <vector>

#include <seqan/basic.h>
#include <seqan/sequence.h>
#include <seqan/seq_io.h>
#include <seqan/random.h>

#include <seqan/arg_parse.h>


using namespace seqan;

// ==========================================================================
// Classes
// ==========================================================================


// ==========================================================================
// Functions
// ==========================================================================


Dna5 select_event(Dna5 base)
{
    /**this function does only gives back a Dna5 char, if the random number i give is in some of the pre-stored intervals, so nothing special**/
    switch(ordValue(base))
    {
        case 'A': return 'T';
        case 'C': return 'G';
        case 'G': return 'C';
        case 'T': return 'A';
        default: return 'N';
    }
}

template <typename TString>
TString replicate2(TString seq, double & innerTime)
{
    std::vector<unsigned> index;
    index.resize(length(seq));
    for (unsigned i = 0; i < index.size(); ++i)
        index[i] = i;

    double start = sysTime();
    for (auto i : index)
//    for (unsigned i = 0; i < index.size(); ++i)
    {
       seq[i] = select_event(seq[i]);
       /**so practically one Dna5 = the other Dna5 variable, with assign() is it even a little slower**/
    }
    innerTime += sysTime() - start;
    return seq;
}

template <typename TString>
inline void replicate3(TString & target, TString const & source, double & innerTime)
{
    std::vector<unsigned> index;
    index.resize(length(source));
    for (unsigned i = 0; i < index.size(); ++i)
        index[i] = i;

    resize(target, length(source), Exact());

    double start = sysTime();
    for (auto i : index)
//    for (unsigned i = 0; i < index.size(); ++i)
    {
       target[i] = select_event(source[i]);
    }
    innerTime += sysTime() - start;
}

// --------------------------------------------------------------------------
// Function main()
// --------------------------------------------------------------------------

// Program entry point.

int main(int argc, char* argv[])
{
    if (argc <= 1)
    {
        std::cerr << "string_bench FILE.fa" << std::endl;
        return 1;
    }
    std::ifstream fileStream(argv[1], std::ios_base::in);
    if (!fileStream.good())
    {
        std::cerr << "Error" << std::endl;
        return 1;
    }

    RecordReader<std::ifstream, SinglePass<> > recReader(fileStream);
    CharString id;
    String<Dna5> buffer;


    if (readRecord(id, buffer, recReader, Fasta()) != 0)
    {
        std::cerr << "Error" << std::endl;
        return 1;
    }

    std::cout << "Seqan String" << std::endl;
    double timeStart = sysTime();
    double innerTime = 0.0;
    for (unsigned i = 0; i < 50; ++i)
        buffer = replicate2(buffer, innerTime);
    std::cout << "Time: " << sysTime() - timeStart << " s. Inner Loop: " << innerTime << " s." << std::endl;

    std::cout << "STL Vector" << std::endl;
    std::vector<Dna5> bufferVec;
    bufferVec.resize(length(buffer));
    arrayCopyForward(begin(buffer), end(buffer), bufferVec.begin());

    timeStart = sysTime();
    double innerTime1 = 0.0;
    for (unsigned i = 0; i < 50; ++i)
        bufferVec = replicate2(bufferVec, innerTime1);
    std::cout << "Time: " << sysTime() - timeStart << " s. Inner Loop: " << innerTime1 << " s." << std::endl;

    std::cout << "STL Basic String Dna5" << std::endl;
    std::basic_string<Dna5> bufferString;
    bufferString.resize(length(buffer));
    arrayCopyForward(begin(buffer), end(buffer), bufferString.begin());

    timeStart = sysTime();
    double innerTime2 = 0.0;
    for (unsigned i = 0; i < 50; ++i)
        bufferString = replicate2(bufferString, innerTime2);
    std::cout << "Time: " << sysTime() - timeStart << " s. Inner Loop: " << innerTime2 << " s." << std::endl;

    std::cout << "STL Basic String Char" << std::endl;
    std::string charString;
    charString.resize(length(buffer));
    arrayCopyForward(begin(buffer), end(buffer), charString.begin());

    timeStart = sysTime();
    double innerTime3 = 0.0;
    for (unsigned i = 0; i < 50; ++i)
        charString = replicate2(charString, innerTime3);
    std::cout << "Time: " << sysTime() - timeStart << " s. Inner Loop: " << innerTime3 << " s." << std::endl;

    std::cout << "The Way We Would Do It" << std::endl;
    String<Dna5> buffer2;
    timeStart = sysTime();
    double innerTime4 = 0.0;
    for (unsigned i = 0; i < 50; ++i)
        replicate3(buffer2, buffer, innerTime4);
    std::cout << "Time: " << sysTime() - timeStart << " s. Inner Loop: " << innerTime4 << " s." << std::endl;

    return 0;
}
