
/*                       
	This file is part of the CVD Library.

	Copyright (C) 2011 The Authors

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

	This library 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
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 
    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "cvd/internal/io/bmp.h"

#include "cvd/image_io.h"
#include "cvd/config.h"
using namespace std;
#include <iostream>

namespace CVD
{
namespace BMP
{

void readBMPHeader(unsigned int& width, unsigned int& height, unsigned int& channels, unsigned int& compression, istream& in);

class ReadPimpl
{
	public:
		ReadPimpl(std::istream&);
		ImageRef size(){ return s;}
		void get_raw_pixel_lines(unsigned char*, unsigned long nlines);
		string datatype()
		{
			return type;
		}
		
		void get_raw_pixel_line(byte* d)
		{
			char dummy[4];
			//Read the data
			i.read((char*)d, s.x);
			
			//Eat the padding
			i.read(dummy, rowSize - s.x);
		}

		void get_raw_pixel_line(Rgb<byte>* d)
		{
			char dummy[4];
			//if(channels == 1)
				//Depalette image
				//Not supported
			
			i.read((char*)d, s.x*3);
			//Turn bgr into rgb
			for(int x=0; x < s.x; x++)
				swap(d[x].red, d[x].blue);

			//Eat the padding
			i.read(dummy, rowSize - s.x*3);
		}

		template<class T> void get_raw_pixel_line(T* d)
		{
			if(datatype() != PNM::type_name<T>::name())
				throw CVD::Exceptions::Image_IO::ReadTypeMismatch(datatype(), PNM::type_name<T>::name());
			get_raw_pixel_line(d);
			//FIXME: check rows.
		}

	private:
		ImageRef s;
		std::istream&	i;
		string type;
		int channels;
		int rowSize;
		bool notgray;
		//Rgb<byte> palette[256];
};

ReadPimpl::ReadPimpl(std::istream& in)
:i(in)
{
	unsigned int w,h,ch, comp;
	readBMPHeader(w,h,ch,comp,in);

	channels=ch;
	s.x = w;
	s.y = h;

	if(channels == 1)
	{
		//Read the palette. Greyscale BMPs are implemented using palettes
		notgray = false;
		for (int i=0; i<256; i++) {
			byte buf[4];
			in.read((char*)buf,4);
			//palette[i].red = buf[2];
			//palette[i].green = buf[1];
			//palette[i].blue = buf[0];
			if (buf[0] != i || buf[0] != buf[1] || buf[1] != buf[2])
				notgray = true;
		}

		if(notgray)
			throw(Exceptions::Image_IO::UnsupportedImageSubType("Windows BMP/DIB", "Nontrivial paletted images."));
		
		rowSize = s.x;
	}
	else
		rowSize = s.x*3;

	if (rowSize % 4)
		rowSize += 4 - (rowSize%4);

	if(channels == 3 || notgray)
		type = PNM::type_name<Rgb<byte> >::name();
	else
		type = PNM::type_name<byte>::name();
}

////////////////////////////////////////////////////////////////////////////////
//
// The public interface
//

ImageRef Reader::size()
{
	return t->size();
}

void Reader::get_raw_pixel_line(unsigned char* d)
{
	t->get_raw_pixel_line(d);
}

void Reader::get_raw_pixel_line(Rgb<byte>* d)
{
	t->get_raw_pixel_line(d);
}

string Reader::datatype()
{
	return t->datatype();
}


bool Reader::top_row_first()
{
	return false;
}
string Reader::name()
{
	return "BMP";
}

Reader::~Reader()
{}

Reader::Reader(std::istream& i)
:t(new ReadPimpl(i))
{}

}
}
