//! [snippet1]
// We include what we need for the test
#include <gatb/gatb_core.hpp>

using namespace std;

/********************************************************************************/
/*                       Solid partitions statistics                            */
/*                                                                              */
/* This snippet computes statistics about the solid kmers of each partition     */
/* generated by the sorting count algorithm.                                    */
/*                                                                              */
/* Cmd-line: kmer11 -in <fasta/q file>                                           */
/*                                                                              */
/* Sample: kmer11 -in gatb-core/gatb-core/test/db/reads1.fa                     */
/*                                                                              */
/********************************************************************************/
int main (int argc, char* argv[])
{
    // We create a command line parser.
    OptionsParser parser ("SortingCount");
    parser.push_back (new OptionOneParam (STR_URI_INPUT,  "sorting count input", true));
    parser.push_back (new OptionOneParam (STR_URI_OUTPUT, "distrib output",      false));
    parser.push_back (new OptionOneParam (STR_NB_CORES,   "number of cores",     false, "0"));

    try
    {
        // Shortcuts.
        typedef Kmer<>::Count           Count;
        typedef Kmer<>::Type            Type;
        typedef Kmer<>::ModelCanonical              ModelCanon;
        typedef Kmer<>::ModelMinimizer<ModelCanon>  Model;

        // We parse the user options.
        IProperties* options = parser.parse (argc, argv);

        // We load the object storing the couples [kmer,abundance]
        Storage* storage = StorageFactory(STORAGE_HDF5).load (options->getStr(STR_URI_INPUT));   LOCAL (storage);

        // We get the group inside the storage object
        Group& dskGroup = storage->getGroup("dsk");

        // We retrieve the partition holding the couples [kmer,abundance]
        Partition<Count>& solidKmers = dskGroup.getPartition<Count> ("solid");

        // We retrieve the repartitor hash function
        Repartitor repart;
        repart.load (storage->getGroup("minimizers"));

        // We get the kmer size
        size_t kmerSize = atol (storage->root().getProperty("kmer_size").c_str());

        // We get a minimizer model
        Model model (kmerSize, 8, typename Kmer<>::ComparatorMinimizerFrequencyOrLex(), repart.getMinimizerFrequencies());

        // We create the output file
        string outputUri = options->get(STR_URI_OUTPUT) ?
            options->getStr(STR_URI_OUTPUT) :
            System::file().getBaseName(options->getStr(STR_URI_INPUT)) + ".distrib";

        FILE* outputFile = fopen (outputUri.c_str(), "w");
        if (outputFile == 0)  { throw Exception ("unable to open output file"); }

        fprintf (outputFile, "#   id   inLocal   inBound     total   %%local  \n");

        // A little bit of parallelization won't hurt
        Dispatcher dispatcher (options->getInt(STR_NB_CORES));

        Range<size_t> range(0, solidKmers.size()-1);

        typedef vector<int> Distrib;

        struct Data
        {
            size_t nbIn;
            size_t nbOut;
            Distrib distrib;
            Distrib neigbhorParts;
            Data (size_t nbSolids) : nbIn(0), nbOut(0), distrib(9), neigbhorParts(nbSolids) {}
        };

        // We customize the progress bar width
        StringLine::setDefaultWidth(8);

        // we read the couples [kmer,abundance] with an iterator over each collection of the partition
        ProgressIterator<size_t> itPart (range);
        for (itPart.first(); !itPart.isDone(); itPart.next())
        {
            // Shortcut
            size_t currentPart = itPart.item();

            ThreadObject<Data>  data (solidKmers.size());

            // We iterate the current collection inside the partition
            dispatcher.iterate (solidKmers[currentPart].iterator(),  [&]  (const Count& count)
            {
                Data& d = data();

                set<int> match;

                // We iterate the neighbors of the current kmer
                model.iterateNeighbors (count.value, [&] (const Type& neighbor)
                {
                    // We get the minimizer value of the current kmer.
                    u_int64_t mini = model.getMinimizerValue(neighbor);

                    // We get the partition index of the neighbor from its minimizer value and the minimizer repartition table.
                    u_int64_t neighborPart = repart (mini);

                    // We look whether or not the neighbor is in the same partition as the current one
                    if (neighborPart != currentPart)  {  match.insert (neighborPart);   d.neigbhorParts[neighborPart]++;  }
                });

                d.distrib [match.size()] ++;

                if (match.size()==0)    { d.nbIn  ++; }
                else                    { d.nbOut ++; }
            });

            for (size_t i=0; i<data->distrib.size();       i++)  { (data->distrib)[i]       = 0; }
            for (size_t i=0; i<data->neigbhorParts.size(); i++)  { (data->neigbhorParts)[i] = 0; }

            data.foreach ([&] (Data& local)
            {
                data->nbIn   += local.nbIn;
                data->nbOut  += local.nbOut;

                for (size_t i=0; i<data->distrib.size();       i++)  { (data->distrib)[i]       += local.distrib[i];        }
                for (size_t i=0; i<data->neigbhorParts.size(); i++)  { (data->neigbhorParts)[i] += local.neigbhorParts[i];  }
            });

            fprintf (outputFile, "%6ld  %8ld  %8ld  %8ld  %7.2f ",
                currentPart ,
                data->nbIn,
                data->nbOut,
                (data->nbIn+data->nbOut),
                100.0 * (double)data->nbIn / (double)(data->nbIn+data->nbOut)
            );

            for (size_t i=0; i<9; i++)  { fprintf(outputFile, "%8d ", (data->distrib)[i]);  }   fprintf (outputFile, "\n");
        }

        fclose (outputFile);
    }
    catch (OptionFailure& e)
    {
        return e.displayErrors (std::cout);
    }
    catch (Exception& e)
    {
        std::cerr << "EXCEPTION: " << e.getMessage() << std::endl;
    }

    return EXIT_SUCCESS;
}
//! [snippet1]


