#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cstring>
#include "debug_macro.hpp"

namespace chocolat{namespace filter{

/** Interface of stream filter function object */
class IStreamFilter
{
public:
	/** this is the body of filter. */
	virtual void operator()(std::istream& is, std::ostream& os)=0;
	/** your filter class needs 'clone' method to be stored in pipe. */
	virtual IStreamFilter* clone()const =0;
	/** this is defined for sub-class. */
	virtual ~IStreamFilter(){}
};

/** It is used in expression, so it's cleared after evaluation. */
class PipeExpression
{
public:
	/** connect input stream to pipe */
	friend PipeExpression operator>>(std::istream& is, PipeExpression pipe);
	/** clear the pointer of input. */
	PipeExpression()
    {
        is = NULL;
    }
	/** copy constructor uses virtual method 'transTo' by default behaves move.*/
	/** this method, using const cast.*/
	PipeExpression(const PipeExpression& pipe)
    {
        PipeExpression* p_p = const_cast<PipeExpression*>(&pipe);
        p_p->transTo(*this);
    }

	/** this is used to move own filters. */
	virtual void transTo(PipeExpression& pipe)
    {
        pipe.is = is;
        for (unsigned int i = 0; i < list.size(); ++i){
            pipe.list.push_back(list[i]);
        }
        list.clear();
    }

	/** delete all own filter object. */
	virtual ~PipeExpression()
    {
        for (unsigned int i = 0; i < list.size(); ++i){
            delete list[i];
        }
    }
	/** push the clone of the pointed filter. */
	void push(const IStreamFilter* p_f)
    {
        list.push_back(p_f->clone());
    }


	/** output to stream */
	std::ostream& operator>>(std::ostream& os)
    {
        std::istream* pin = is;
        std::ostream* pout;
        std::stringstream* pss = NULL;
        for (unsigned int i=0; i<list.size(); ++i){
            if (i == list.size()-1){
                pout = &os;
            } else {
                pss = new std::stringstream();
                pout = pss;
            }
            (*list[i])(*pin,*pout);
            if (pin && pin != is) {
                delete pin;
                pin = NULL;
            }
            pin = pss;
        }
        return os;
    }

    
	/** connect to pipe, returns new pipe object. */
	PipeExpression operator>>(const PipeExpression& ref)const
    {
        PipeExpression a(ref);
        PipeExpression b(*this);
        for (unsigned int i=0; i<a.list.size(); ++i){
            b.push(a.list[i]);
        }
        a.list.clear();
        return b;
    }
	/** connect to filter, returns new pipe object.*/
	PipeExpression operator>>(const IStreamFilter& f)const
    {
        PipeExpression a(*this);
        a.push(&f);
        return a;
    }


	
	/** connected input stream */
	std::istream* is;
	/** connected filter list */
	std::vector<IStreamFilter*> list;
};

PipeExpression
operator>>(std::istream& is, PipeExpression pipe)
{
	pipe.is = &is;
	return pipe;
}

/** connect input stream to filter, returns pipe */
PipeExpression
operator>>(std::istream& is, const IStreamFilter& f)
{
	PipeExpression pipe;
	pipe.is = &is;
	pipe.push(&f);
	return pipe;
}


/** This class is used to save the pipe.
 *  If you need temporary, use PipeExpression.
 */
class Pipe : public PipeExpression
{
public:
	/** filter pipe copies filter to other */
	virtual void transTo(PipeExpression& pipe)
    {
        for (unsigned int i = 0; i < list.size(); ++i){
            pipe.push(list[i]);
        }
        pipe.is = is;
    }

	/** in assignment, it copies the filter objects. */
	Pipe& operator=(const PipeExpression& pipe){
        for (unsigned int i = 0; i < pipe.list.size(); ++i){
            push(pipe.list[i]);
        }
        is = pipe.is;
        return *this;
    }

private:
	/** copy constructor is prohibited. */
	Pipe(const PipeExpression& copy);
};
		
}}
