/*
	This file is part of Overmix.

	Overmix is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Overmix is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Overmix.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef A_CONTAINER_HPP
#define A_CONTAINER_HPP

#include "../Geometry.hpp"
#include <vector>
#include <utility>

namespace Overmix{

class ImageEx;
class Plane;
class AComparator;
struct ImageOffset;
enum class ScalingFunction;

template<class PointerType>
class ContainerImageRef{
	protected:
		PointerType* parent;
		unsigned index;

	public:
		ContainerImageRef( PointerType& parent, unsigned index )
			: parent( &parent ), index(index) {}

		auto& image()     const{ return parent->image(     index ); }
		auto& imageRef()       { return parent->imageRef(  index ); }
		auto& alpha()     const{ return parent->alpha(     index ); }
		auto  imageMask() const{ return parent->imageMask( index ); }
		auto  rawPos()    const{ return parent->rawPos(    index ); }
		auto  frame()     const{ return parent->frame(     index ); }
		auto& plane()     const{ return parent->plane(     index ); }
		auto  zoom()      const{ return parent->zoom(      index ); }
		auto  rotation()  const{ return parent->rotation(  index ); }
		
		auto transformedPos()    const{ return parent->transformedPos(    index ); }
		auto transformedSize()   const{ return parent->transformedSize(   index ); }
		bool requiresTransform() const{ return parent->requiresTransform( index ); }
		
		void  setRawPos( Point<double> newVal ) { parent->setRawPos(   index, newVal ); }
		void  setFrame(int newVal )          { parent->setFrame( index, newVal ); }
		void  setZoom( Point<double> newVal ){ parent->setZoom(  index, newVal ); }
		void  setRotation( double newVal    ){ parent->setRotation( index, newVal ); }
};


class AContainer{
	public:
		virtual unsigned count() const = 0;
		virtual const ImageEx& image(     unsigned index ) const = 0;
		virtual       ImageEx& imageRef(  unsigned index )       = 0;
		virtual const Plane&   alpha(     unsigned index ) const = 0;
		virtual       int      imageMask( unsigned index ) const;
		virtual Point<double>  rawPos(    unsigned index ) const = 0;
		virtual       void     setRawPos( unsigned index, Point<double> newVal ) = 0;
		virtual       int      frame(     unsigned index ) const = 0;
		virtual       void     setFrame(  unsigned index, int newVal ) = 0;
		
		virtual Point<double>  zoom(      unsigned index ) const = 0;
		virtual       void     setZoom(   unsigned index, Point<double> newVal ) = 0;
		
		virtual       double   rotation(  unsigned index ) const = 0;
		virtual       void     setRotation(unsigned index, double newVal ) = 0;
		
		
		virtual const Plane&   mask(      unsigned index ) const;
		virtual       void     setMask(   unsigned index, int id ) = 0;
		virtual unsigned maskCount() const{ return 0; }
        
		virtual ~AContainer() { }
		
		virtual const Plane& plane( unsigned index ) const;
		
	public:
		virtual Point<double> getHint( unsigned, unsigned );
		virtual const AComparator* getComparator() const = 0;
		virtual bool        hasCachedOffset( unsigned, unsigned ) const{ return false; }
		virtual ImageOffset getCachedOffset( unsigned, unsigned ) const;
		virtual void        setCachedOffset( unsigned, unsigned, ImageOffset );
		ImageOffset findOffset( unsigned, unsigned );
		double findError( unsigned, unsigned );
		
		bool requiresTransform( unsigned index ) const;
		Point<double> transformedPos( unsigned index ) const;
		Size<unsigned> transformedSize( unsigned index ) const;
		
	public:
		Rectangle<double> size() const;
		Point<double> minPoint() const;
		Point<double> maxPoint() const;
		std::pair<bool,bool> hasMovement() const;
		void resetPosition()
			{ for( unsigned i=0; i<count(); i++ ) setRawPos( i, { 0.0, 0.0 } ); }
		void offsetAll( Point<double> offset )
			{ for( unsigned i=0; i<count(); i++ ) setRawPos( i, rawPos( i ) + offset ); }
		std::vector<int> getFrames() const;
		
		//Indexed access, including typedefs
		using ConstRef = ContainerImageRef<const AContainer>;
		using      Ref = ContainerImageRef<      AContainer>;
		auto operator[]( unsigned index ) const{ return ConstRef( *this, index );}
		auto operator[]( unsigned index )      { return      Ref( *this, index );}
		
};

/* We add the iterator members here, so we can't accidently use them */
template<class PointerType>
class ContainerImageIterator : public ContainerImageRef<PointerType>{
    public:
        ContainerImageIterator( PointerType& container, unsigned index )
            :   ContainerImageRef<PointerType>( container, index ) { }
        
        auto& operator++(){
			this->index++;
			return *this;
		}
        ContainerImageRef<PointerType> operator*(){ return { *(this->parent), this->index }; }
        bool operator!=( ContainerImageIterator b ){ return this->parent != b.parent || this->index != b.index; }
};

// All the container iterators
inline ContainerImageIterator<      AContainer> begin(       AContainer& container ){ return { container, 0                 }; }
inline ContainerImageIterator<const AContainer> begin( const AContainer& container ){ return { container, 0                 }; }
inline ContainerImageIterator<      AContainer> end(         AContainer& container ){ return { container, container.count() }; }
inline ContainerImageIterator<const AContainer> end(   const AContainer& container ){ return { container, container.count() }; }

}

#endif
