#include "htio2/FastqIO.h"
#include "htio2/FastqSeq.h"
#include "htio2/HeaderUtil.h"

#define USE_prefix_out
#define USE_header_format
#include "htqc/Options.h"
#include "htqc/App.h"

#include <set>

using namespace std;
using namespace htqc;

typedef set<int16_t> IntSet;
typedef vector<int16_t> IntList;

IntList OPT_exclude_lane;
htio2::Option OPT_exclude_lane_ENTRY("lane", 'L', "Filter Options",
                                     &OPT_exclude_lane, htio2::Option::FLAG_MULTI_KEY, htio2::ValueLimit::Free(),
                                     "Unwanted lanes.", "LANE1 [LANE2 ...]");

IntList OPT_exclude_tile;
htio2::Option OPT_exclude_tile_ENTRY("tile", 'T', "Filter Options",
                                     &OPT_exclude_tile, htio2::Option::FLAG_MULTI_KEY, htio2::ValueLimit::Free(),
                                     "Unwanted tiles.", "TILE1 [TILE2 ...]");

string OPT_prefix_reject;
htio2::Option OPT_prefix_reject_ENTRY("reject", 'u', OPTION_GROUP_IO,
                                      &OPT_prefix_reject, htio2::Option::FLAG_NONE,
                                      "File prefix for rejected sequences.", "FILE_PREFIX");


void parse_options(int argc, char **argv)
{
    htio2::OptionParser opt_psr;

    opt_psr.add_option(OPT_files_in_ENTRY);
    opt_psr.add_option(OPT_prefix_out_ENTRY);
    opt_psr.add_option(OPT_prefix_reject_ENTRY);

    opt_psr.add_option(OPT_exclude_lane_ENTRY);
    opt_psr.add_option(OPT_exclude_tile_ENTRY);

    opt_psr.add_option(OPT_header_format_ENTRY);
    opt_psr.add_option(OPT_header_sra_ENTRY);

    opt_psr.add_option(OPT_quiet_ENTRY);
    opt_psr.add_option(OPT_help_ENTRY);
    opt_psr.add_option(OPT_version_ENTRY);

    opt_psr.parse_options(argc, argv);

    // show help
    if (OPT_help)
    {
        cout << endl
             << argv[0] << " - filter sequences from specific lane-tile"
             << endl << endl
             << opt_psr.format_document();
        exit(EXIT_SUCCESS);
    }

    if (OPT_version) show_version_and_exit();
}

void validate_options()
{
    ensure_files_in();
    ensure_prefix_out();
    ensure_header_format();

    tidy_prefix_out(OPT_prefix_reject);
    if (OPT_prefix_out == OPT_prefix_reject)
    {
        cerr << "ERROR: conflict output prefix for accepted and rejected reads: " << OPT_prefix_out << endl;
        exit(EXIT_FAILURE);
    }

    if (OPT_exclude_tile.size() == 0)
    {
        cerr << "ERROR: tiles to exclude not specified" << endl;
        exit(EXIT_FAILURE);
    }
}

void show_options()
{
    show_files_in();
    show_prefix_out();
    cout << "# output prefix for rejected sequences: " << OPT_prefix_reject << endl;

    cout << "# exclude lanes: " << endl
         << "#   ";
    for (size_t i = 0; i < OPT_exclude_lane.size(); i++)
    {
        if (i != 0) cout << ", ";
        cout << OPT_exclude_lane[i];
    }
    cout << endl;

    cout << "# wanted tiles: " << endl
         << "#   ";
    for (size_t i = 0; i < OPT_exclude_tile.size(); i++)
    {
        if (i != 0) cout << ", ";
        cout << OPT_exclude_tile[i];
    }
    cout << endl;
}

void execute()
{
    string file_out;
    add_fastq_suffix_se(OPT_prefix_out, OPT_compress, file_out);
    check_output_overwrite(OPT_files_in, file_out);
    htio2::FastqIO fh_out(file_out, htio2::WRITE, OPT_compress);

    htio2::FastqIO::Ptr fh_reject;
    if (OPT_prefix_reject.length())
    {
        string file_reject;
        add_fastq_suffix_se(OPT_prefix_reject, OPT_compress, file_reject);
        fh_reject = new htio2::FastqIO(file_reject, htio2::WRITE, OPT_compress);
    }

    IntSet lane_search(OPT_exclude_lane.begin(), OPT_exclude_lane.end());
    IntSet tile_search(OPT_exclude_tile.begin(), OPT_exclude_tile.end());

    htio2::HeaderParser header_parser(OPT_header_format, OPT_header_sra);

    htio2::FastqSeq seq;
    size_t n_process = 0;
    size_t n_accept = 0;
    for (size_t i_file = 0; i_file < OPT_files_in.size(); i_file++)
    {
        htio2::FastqIO fh_in(OPT_files_in[i_file], htio2::READ, OPT_compress);

        while (fh_in.next_seq(seq))
        {
            n_process++;
            header_parser.parse(seq.id, seq.desc);

            if ((lane_search.empty() || lane_search.count(header_parser.lane)) &&
                     tile_search.count(header_parser.tile))
            {
                if (fh_reject)
                    fh_reject->write_seq(seq);
            }
            else
            {
                fh_out.write_seq(seq);
                n_accept++;
            }

            if (!OPT_quiet and (n_process%LOG_BLOCK == 0))
                cout << "  " << n_process << " reads, " << n_accept << " accept" << endl;
        }
    }

    if (!OPT_quiet)
        cout << "  " << n_process << " reads, " << n_accept << " accept" << endl;
}
