/*
 * Copyright (c) 1999 Johnny C. Lam.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  The author makes no representations
 * about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 */

#include <algorithm>
#include <array>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>

#include "commandline.h"
#include "dictionary.h"
#include "geometric.h"
#include "haplotype.h"
#include "path_strategy_factory.h"
#include "probability.h"
#include "segment_factory.h"
#include "types.h"

int
main(int argc, char* argv[])
{
    CommandLine::options_t options[] = {
	CommandLine::VERBOSE,
	CommandLine::SEGMENT,
	CommandLine::LEFT_MASK,
	CommandLine::RIGHT_MASK,
	CommandLine::SSM,
	CommandLine::IBD,
	CommandLine::P_MUTATION,
	CommandLine::P_RECOMBINATION,
	CommandLine::DIST_FNAME,
	CommandLine::STR_FNAME,
	CommandLine::RECOMB_FNAME,
	CommandLine::NO_RECOMB_SUM,
	CommandLine::MAX_LATENT_DEPTH,
	CommandLine::PATH_STRATEGY,
	CommandLine::ALLELE_FREQ,
	CommandLine::NUM_NORMALS,
	CommandLine::NORMAL_FNAME
    };

    CommandLine::options_t required[] = {
	CommandLine::NORMAL_FNAME
    };
    
    CommandLine::Instance()->Parse(argc, argv,
				   begin_array(options), end_array(options),
				   begin_array(required), end_array(required));
    
    std::cout << "Initialization." << endl;
    if (CommandLine::Instance()->Verbose() > 0) {
	CommandLine::Instance()->Write(std::cout,
				       begin_array(options), end_array(options));
    }
    
    auto_ptr<SegmentProbability> segment(
	SegmentProbabilityFactory::Instance()->Make(
	    CommandLine::Instance()->Segment()));

    auto_ptr<HDictionary> dictionary(new HDictionary(segment.get()));
    {
	ifstream istr(CommandLine::Instance()->NormalFname().data());
	if (CommandLine::Instance()->AlleleFreq()) {
	    dictionary->ReadFrequencies(istr,
					CommandLine::Instance()->NumNormals());
	} else {
	    dictionary->ReadDelimited(istr, '\n',
				      CommandLine::Instance()->NumNormals());
	}
    }

    auto_ptr<Probability> rules;
    auto_ptr<PathStrategy> strategy;
    
    Haplotype	src;
    Haplotype	dest;
    int		disease;
    
    while (true) {
	std::cout << "Enter source ('999' to quit): "; std::cout.flush();
	src.ReadDelimited(std::cin, Haplotype::DISEASED);
	if (src[0] == 999)
	    break;
	
	std::cout << "Enter target ('999' to quit): "; std::cout.flush();
	dest.ReadDelimited(std::cin, Haplotype::DISEASED);
	if (dest[0] == 999)
	    break;
	
	unsigned int rmask = std::min(CommandLine::Instance()->RightMask(),
				      src.size());

	src.Mask(CommandLine::Instance()->LeftMask(), rmask);
	dest.Mask(CommandLine::Instance()->LeftMask(), rmask);
	
	std::cout << "Enter disease location ('999' to quit): ";
	std::cout.flush();
	std::cin >> disease;
	if (disease == 999)
	    break;
	
	if (!rules.get()) {
	    rules.reset(
		new Probability(Geometric(CommandLine::Instance()->Ssm()),
				dictionary.get(),
				CommandLine::Instance()->Ibd(),
				CommandLine::Instance()->PMutation(),
				CommandLine::Instance()->PRecombination(),
				src.size(),
				CommandLine::Instance()->LeftMask(),
				rmask,
				CommandLine::Instance()->StrFname(),
				CommandLine::Instance()->DistFname(),
				CommandLine::Instance()->RecombFname(),
				CommandLine::Instance()->NoRecombSum(),
				(CommandLine::Instance()->Verbose() > 1)));
	}
	if (!strategy.get()) {
	    strategy =
		PathStrategyFactory::Instance()->Make(
		    CommandLine::Instance()->PathStrategy(),
		    CommandLine::Instance()->MaxLatentDepth(),
		    (CommandLine::Instance()->Verbose() > 0));
	}
	
	Path path = strategy->MakePath(src, dest, disease, rules.get());
	std::cout << path << endl
		  << "Path Weight: " << path.Weight() << endl;
    }
    
    return 0;
}
