/*
 * 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 "haplotype.h"

#include <algorithm>
#include <cstring>
#include <iostream>
#include <iterator>
#include <strstream>

INITIALIZE_SINGLETON_BASE(HaplotypeProperties);

Haplotype
Haplotype::Substr(size_type start, size_type end) const
{
    return Haplotype(const_iterator_at(start), const_iterator_at(end));
}

void
Haplotype::Mask(size_type left_mask, size_type right_mask)
{
    property_t old_properties = Properties();

    if (_value->is_shared())
	_value = new HaplotypeValue(*_value);

    for (size_type i = 0; i < left_mask; ++i)
	_value->loci[i] = Marker::NA;

    size_type end = size();
    right_mask = min(right_mask, size());
    for (size_type i = right_mask; i < end; ++i)
	_value->loci[i] = Marker::NA;

    HaplotypeProperties::Instance()->SetProperties(*this, old_properties);
}

void
Haplotype::LeftRecombine(size_type gap, const Haplotype& h)
{
    if (_value->is_shared())
	_value = new HaplotypeValue(*_value);
    for (size_type k = 0; k < gap; ++k)
	_value->loci[k] = h[k];
}

void
Haplotype::RightRecombine(size_type gap, const Haplotype& h)
{
    if (_value->is_shared())
	_value = new HaplotypeValue(*_value);
    for (size_type k = gap; k < size(); ++k)
	_value->loci[k] = h[k];
}


///////////////////////////////////////////////////////////////////////////
//
// void	Read( istream & )
//
// Reads haplotypes of the following forms from the istream.
//      ( L1 L2 ... Ln )
//      ( L1 L2 ... Ln [L] )
//      ( L1 L2 ... Ln [N] )
//      ( L1 L2 ... Ln [L] [N] )
//
///////////////////////////////////////////////////////////////////////////

void
Haplotype::Read(istream& istr)
{
    char c;
    property_t properties = NONE;
    bool latent = false;
    
    istr >> ws >> c;		// left paren
    istr >> ws >> c;		// potential ) or [
    container_type loci;
    value_type m;
    while (c != ')' && c != '[') {
	istr.putback(c);
	istr >> m;		// marker
	loci.push_back(m);
	istr >> ws >> c;	// potential ) or [
    }

    while (c != ')') {		// [
	istr >> ws >> c;	// L/N
	switch (c) {
	case 'L':
	    properties |= LATENT;
	    break;
	case 'N':
	    properties |= NORMAL;
	    break;
	}
	istr >> ws >> c;	// ]
	istr >> ws >> c;	// potential )
    }

    if (_value->is_shared()) {
	_value = new HaplotypeValue(loci);
    } else {
	_value->loci.swap(loci);
    }

    if (properties == NONE)
	properties = DISEASED;

    HaplotypeProperties::Instance()->SetProperties(*this, properties);
}

///////////////////////////////////////////////////////////////////////////
//
// bool ReadDelimited( istream &, char, property_t )
//
// Read a delimiter-ended haplotype from the istream:
//
// 	L1 L2 ... Ln delimiter
//
// If one of the "markers" is "L" then, the haplotype is considered latent.
// If one of the "markers" is "N" then, the haplotype is considered normal.
// Returns true if the read was successful and false otherwise.
// Eats the delimiter.
//
///////////////////////////////////////////////////////////////////////////

bool
Haplotype::ReadDelimited(istream& istr, property_t default_properties,
			 char delimiter)
{
    container_type loci;
    value_type marker;
    char c;

    istr >> ws;
    
    if (istr.eof())
	return false;

    property_t properties = NONE;

    char s[256];
    istr.getline(s, 255, delimiter);
    istrstream is(s);
    while (!is.eof()) {
	is >> c;
	switch (c) {
	case 'L':
	    properties |= LATENT;
	    is >> ws;
	    continue;
	default:
	    is.putback(c);
	    is >> marker >> ws;
	    loci.push_back(marker);
	}
    }

    if (_value->is_shared())
	_value = new HaplotypeValue(loci);
    else
	_value->loci.swap(loci);

    properties |= HaplotypeProperties::Instance()->Properties(*this);
    properties |= default_properties;

    HaplotypeProperties::Instance()->SetProperties(*this, properties);
    
    return true;
}

bool
operator==(const Haplotype& lhs, const Haplotype& rhs)
{
    if (lhs.size() == 0) {
	if (rhs.size() == 0) {
	    return true;
	} else {
	    return false;
	}
    } else if (rhs.size() == 0) {
	return false;
    } else if (lhs.size() < rhs.size()) {
	return equal( lhs.begin(), lhs.end(), rhs.begin() );
    } else {
	return equal( rhs.begin(), rhs.end(), lhs.begin() );
    }
}

ostream&
operator<<(ostream& ostr, const Haplotype& h)
{
    ostr << "( ";
    copy(h.begin(), h.end(), ostream_iterator<Marker>(ostr, " "));
    Haplotype::property_t properties = h.Properties();
    if (h.IsLatent())
	ostr << "[L] ";
    if (properties & Haplotype::NORMAL)
	ostr << "[N] ";
    ostr << ")";
    return ostr;
}
