//***************************************************************************
//* Copyright (c) 2015 Saint Petersburg State University
//* Copyright (c) 2011-2014 Saint Petersburg Academic University
//* All Rights Reserved
//* See file LICENSE for details.
//***************************************************************************

#pragma once

#include "single_read.hpp"
#include "delegating_reader_wrapper.hpp"

namespace io {

//todo refactor!!!
class IdSettingReaderWrapper: public DelegatingWrapper<SingleRead> {
    typedef DelegatingWrapper<SingleRead> base;
    size_t next_id_;
public:
    IdSettingReaderWrapper(base::ReadStreamPtrT reader, size_t start_id = 0) :
            base(reader), next_id_(start_id) {
    }

    /* virtual */
    IdSettingReaderWrapper& operator>>(SingleRead& read) {
        this->reader() >> read;
        read.ChangeName(std::to_string(next_id_++));
        return *this;
    }
};

class PrefixAddingReaderWrapper: public DelegatingWrapper<SingleRead> {
    typedef DelegatingWrapper<SingleRead> base;
    std::string prefix_;
public:
    PrefixAddingReaderWrapper(base::ReadStreamPtrT reader,
            const std::string& prefix) :
            base(reader), prefix_(prefix) {
    }

    /* virtual */
    PrefixAddingReaderWrapper& operator>>(SingleRead& read) {
        this->reader() >> read;
        read.ChangeName(prefix_ + read.name());
        return *this;
    }
};

//fixme currently leads to long stretches of ACGTACGT...
class FixingWrapper: public DelegatingWrapper<SingleRead> {
    typedef DelegatingWrapper<SingleRead> base;

    io::SingleRead MakeValid(const io::SingleRead& read) const {
        std::string str = read.GetSequenceString();
        for (size_t i = 0; i < str.length(); ++i) {
            if (!is_nucl(str[i]))
                str[i] = nucl(char(i % 4));
        }
        return io::SingleRead(read.name(), str);
    }

public:
    FixingWrapper(base::ReadStreamPtrT reader) :
            base(reader) {
    }

    /* virtual */
    FixingWrapper& operator>>(SingleRead& read) {
        this->reader() >> read;
        if (!read.IsValid()) {
            TRACE("Read " << read.name() << " was invalid. Fixing");
            read = MakeValid(read);
            VERIFY(read.IsValid());
        }
        return *this;
    }

private:
    DECL_LOGGER("FixingWrapper");
};

class NonNuclCollapsingWrapper: public DelegatingWrapper<SingleRead> {
    typedef DelegatingWrapper<SingleRead> base;

    io::SingleRead MakeValid(const io::SingleRead& read) const {
        std::string str = read.GetSequenceString();
        std::stringstream ss;
        for (size_t i = 0; i < read.size(); ++i) {
            if (is_nucl(str[i]))
                ss << str[i];
        }
        return io::SingleRead(read.name(), ss.str());
    }

public:
    NonNuclCollapsingWrapper(base::ReadStreamPtrT reader) :
            base(reader) {
    }

    /* virtual */
    NonNuclCollapsingWrapper& operator>>(SingleRead& read) {
        this->reader() >> read;
        if (!read.IsValid()) {
            TRACE("Read " << read.name() << " was invalid. Collapsing non-nucls");
            read = MakeValid(read);
            VERIFY(read.IsValid());
        }
        return *this;
    }

private:
    DECL_LOGGER("NonNuclCollapsingWrapper");
};

}
