#include "Pliki.h"

#include <stdio.h>

bool CzyPlikIstnieje(const char* filename)
{
	FILE* plik_test;
	if (fopen_s(&plik_test, filename, "r") != 0) return false;
	else fclose(plik_test);
	return true;
}

#include <stdexcept>

unsigned long* WczytajObrazZPlikuBitmap(HWND uchwytOkna, char* nazwaPliku, int& szerokośćObrazu, int& wysokośćObrazu, bool czytajZZasobów, unsigned char alfa)
{
	//niezbyt elegancko jest tworzyc new w funkcji (zapomina sie o delete)
	//ale dzieki temu nie trzeba rozbijac na dwie czesci (pobieranie obrazu i czytanie)
	//nie trzeba zwracac informacji o obrazie, bo zawsze zwracamy tablice RGBA
	try
	{
		//czytanie bitmapy z pliku lub z zasobow
		char komunikat[1024] = "Brak pliku obrazu ";
		strcat_s(komunikat, nazwaPliku);
		if (!czytajZZasobów && !CzyPlikIstnieje(nazwaPliku)) throw std::invalid_argument(komunikat);
		HBITMAP uchwytObrazu = (HBITMAP)LoadImage(GetModuleHandle(NULL), nazwaPliku, IMAGE_BITMAP, 0, 0, (czytajZZasobów ? 0 : LR_LOADFROMFILE) | LR_CREATEDIBSECTION);

		//informacje o bitmapie
		BITMAP obraz;
		GetObject(uchwytObrazu, sizeof(BITMAP), &obraz);
		szerokośćObrazu = obraz.bmWidth;
		wysokośćObrazu = obraz.bmHeight;

		//informacja o ilosci bitow na piksel
		//char bufor[256];
		//ShowMessage(_gcvt(obraz.bmBitsPixel,10,bufor));		

		unsigned long* piksele = new unsigned long[szerokośćObrazu*wysokośćObrazu];
		switch (obraz.bmBitsPixel)
		{
		case 24:
		{
			unsigned char* piksele24bppBRG = new unsigned char[obraz.bmWidthBytes*wysokośćObrazu];
			memcpy(piksele24bppBRG, obraz.bmBits, szerokośćObrazu*wysokośćObrazu * 3);
			//gdybym zwrocil tekstura24bppBRG zrzutowane na unsigned long*, to powinienem uzywac do wyswietlania GL_BGR_EXT

			//konwersja na RGBA
			for (int ih = 0; ih<wysokośćObrazu; ih++)	//kolejnosc i tak dowolna, bo tablica jednowymiarowa	
				for (int iw = 0; iw<szerokośćObrazu; iw++)
				{
					int i = 3 * iw + (ih*obraz.bmWidthBytes); //uwzglednia uzupelnianie do WORD
					unsigned char A = alfa;
					unsigned char B = piksele24bppBRG[i];
					unsigned char G = piksele24bppBRG[i + 1];
					unsigned char R = piksele24bppBRG[i + 2];

					/*
					//jezeli konwersja na BW
					unsigned char jasnosc=(R+G+B)/3;
					R=jasnosc;
					B=jasnosc;
					G=jasnosc;
					*/

					piksele[iw + (ih*szerokośćObrazu)] = (A << 24) + (B << 16) + (G << 8) + (R);
				}
			delete[] piksele24bppBRG;
		}
			break;

		case 1:	//monochromatyczne
		{
			unsigned char* piksele1bppMono = new unsigned char[obraz.bmWidthBytes*wysokośćObrazu];
			memcpy(piksele1bppMono, obraz.bmBits, obraz.bmWidthBytes*wysokośćObrazu);

			//konwersja na RGBA				
			for (int ih = 0; ih<wysokośćObrazu; ih++)
				for (int iw = 0; iw<szerokośćObrazu; iw++)
				{
					int i = iw / 8 + (ih*obraz.bmWidthBytes); //uwzglednia uzupelnianie do WORD
					int numerBitu = iw % 8;
					unsigned char A = alfa;
					bool bitZapalony = ((piksele1bppMono[i] << numerBitu) & 128) == 128;
					//ignorujemy palete i tworzymy obraz czarno-bialy
					unsigned char B = bitZapalony ? 255 : 0;
					unsigned char G = bitZapalony ? 255 : 0;
					unsigned char R = bitZapalony ? 255 : 0;
					piksele[iw + (ih*szerokośćObrazu)] = (A << 24) + (B << 16) + (G << 8) + (R);
				}
			delete[] piksele1bppMono;
		}
			break;

		case 8: //256 kolorow, wymaga palety barw (tabeli kolorow)
		{
			unsigned char* piksele8bppPalette = new unsigned char[obraz.bmWidthBytes*wysokośćObrazu];
			memcpy(piksele8bppPalette, obraz.bmBits, obraz.bmWidthBytes*wysokośćObrazu);
			//memcpy(tekstura,tekstura8bppPalette,obraz.bmWidthBytes*wysokośćObrazu);				

			//pobranie tabeli kolorow (pomijamy czytanie BITMAPINFO)
			HDC uchwyt = CreateCompatibleDC(GetDC(uchwytOkna));
			SelectObject(uchwyt, uchwytObrazu);
			RGBQUAD tabelaKolorow[256];
			GetDIBColorTable(uchwyt, 0, 256, tabelaKolorow);

			/*
			//standardowa paleta kolorow z systemu
			//HPALETTE uchwytPalety=(HPALETTE)GetStockObject(DEFAULT_PALETTE);
			PALETTEENTRY tabelaKolorow[256];
			GetSystemPaletteEntries(GetDC(uchwytOkna),0,256,tabelaKolorow);
			*/

			for (int ih = 0; ih<wysokośćObrazu; ih++)	//kolejnosc i tak dowolna, bo tablica jednowymiarowa	
				for (int iw = 0; iw<szerokośćObrazu; iw++)
				{
					int i = iw + (ih*obraz.bmWidthBytes); //uwzglednia uzupelnianie do WORD
					unsigned char A = alfa;
					unsigned char R = tabelaKolorow[piksele8bppPalette[i]].rgbRed;
					unsigned char G = tabelaKolorow[piksele8bppPalette[i]].rgbGreen;
					unsigned char B = tabelaKolorow[piksele8bppPalette[i]].rgbBlue;
					piksele[iw + (ih*szerokośćObrazu)] = (A << 24) + (B << 16) + (G << 8) + (R);
				}
			delete[] piksele8bppPalette;
		}
			break;

		case 4: //16 kolorow (paleta barw jak w Paint)
		{
			unsigned char* piksele4bppPalette = new unsigned char[obraz.bmWidthBytes*wysokośćObrazu];
			memcpy(piksele4bppPalette, obraz.bmBits, obraz.bmWidthBytes*wysokośćObrazu);

			//pobranie tabeli kolorow (pomijamy czytanie BITMAPINFO)
			HDC uchwyt = CreateCompatibleDC(GetDC(uchwytOkna));
			SelectObject(uchwyt, uchwytObrazu);
			RGBQUAD tabelaKolorow[16];
			GetDIBColorTable(uchwyt, 0, 16, tabelaKolorow);

			for (int ih = 0; ih<wysokośćObrazu; ih++)	//kolejnosc i tak dowolna, bo tablica jednowymiarowa	
				for (int iw = 0; iw<szerokośćObrazu; iw++)
				{
					int i = iw / 2 + (ih*obraz.bmWidthBytes); //uwzglednia uzupelnianie do WORD
					bool pierwszaPolowaBajtu = !(iw % 2);
					unsigned char A = alfa;
					int numerKoloruZPalety = (pierwszaPolowaBajtu) ? ((piksele4bppPalette[i] & 0xF0) >> 4) : (piksele4bppPalette[i] & 0x0F);
					unsigned char R = tabelaKolorow[numerKoloruZPalety].rgbRed;
					unsigned char G = tabelaKolorow[numerKoloruZPalety].rgbGreen;
					unsigned char B = tabelaKolorow[numerKoloruZPalety].rgbBlue;
					piksele[iw + (ih*szerokośćObrazu)] = (A << 24) + (B << 16) + (G << 8) + (R);
				}
			delete[] piksele4bppPalette;
		}
			break;

		default: throw std::exception("Nieobsługiwany format bitmapy"); break;
		}

		DeleteObject(uchwytObrazu);
		return piksele;
	}
	catch (const std::exception& exc)
	{
		char komunikat[256] = "Błąd podczas pobierania tekstury:\n";
		strcat_s(komunikat, exc.what());
		MessageBox(NULL, komunikat, "Błąd teksturowania", NULL);
		return NULL;
	}
}
