//
// Copyright (c) 1998 Colosseum Builders, Inc.
// All rights reserved.
//
// Colosseum Builders, Inc. makes no warranty, expressed or implied
// with regards to this software. It is provided as is.
//
// Permission to use, redistribute, and copy this file is granted
// without a fee so long as as the following conditions are adhered to:
//
// o The user assumes all risk for using this software. The authors of this
//   software shall be liable for no damages of any kind.
//
// o If the source code is distributed then this copyright notice must
//   remain unaltered and any modification must be noted.
//
// o If this code is shipped in binary format the accompanying documentation
//   should state that "this software is based, in part, on the work of
//   Colosseum Builders, Inc."
//

//
//  Title:  Sample Image Viewer/Format Conversion Application
//
//  Author:  John M. Miano miano@colosseumbuilders.com
//
//  Description:
//
//    MDI Child Class
//
//---------------------------------------------------------------------
#include <vcl.h>

#include <fstream>
#include <cerrno>
#pragma hdrstop

#include "ChildWin.h"
#include "main.h"

//---------------------------------------------------------------------
#pragma resource "*.dfm"

using namespace Colosseum ;

static void ProgressFunction (Colosseum::BitmapImageCoder &coder,
                              void *data,
                              unsigned int currentpass,
                              unsigned int passcount,
                              unsigned int progress,
                              bool &cancel)
{
  unsigned int value = (100 * (currentpass - 1) + progress)
                               / passcount ;
  MainForm->ProgressBar->Position = value ;
  if (progress == 100 && MainForm->ShowProgressive)
  {
    coder.updateImage () ;
    TMDIChild *child = (TMDIChild *) data ;
    child->image_type = Colosseum::JpegImage ;
    child->PaintBox->SetBounds (0, 0, child->image.getWidth (),
                                child->image.getHeight ()) ;
    child->LoadBitmapInfoHeader () ;
    child->PaintBox->Refresh () ;
  }
  Application->ProcessMessages () ;
  return ;
}

void ImageProgressFunction (Colosseum::BitmapImage &image,
                              void *data,
                              unsigned int currentpass,
                              unsigned int passcount,
                              unsigned int progress,
                              bool &cancel)
{
  MainForm->ProgressBar->Position = (100 * (currentpass - 1) + progress)
                               / passcount ;
  Application->ProcessMessages () ;
  return ;
}

//---------------------------------------------------------------------
__fastcall TMDIChild::TMDIChild(TComponent *Owner)
	: TForm(Owner)
{
  image_type = Colosseum::UnknownImage ;
  gamma_value = 1.0 ;
  is_busy = false ;
  return ;
}
//---------------------------------------------------------------------
void __fastcall TMDIChild::FormClose(TObject *Sender, TCloseAction &Action)
{
  if (! is_busy)
    Action = caFree;
  return ;
}
//---------------------------------------------------------------------
void TMDIChild::ReadImage (const String &filename)
{
  is_busy = true ;

  MainForm->ShowProgressBar () ;
  Cursor = crHourGlass ;
  Caption = filename ;
  image_type = Colosseum::UnknownImage ;

  try
  {
    image_type = Colosseum::ReadImage (
                             filename.c_str (),
                             image,
                             ProgressFunction,
                             reinterpret_cast<void*>(this)) ;
  }
  catch (std::exception &ee)
  {
    Caption = "" ;
    Cursor = crDefault ;
    MainForm->ProgressBar->Visible = false ;
    is_busy = false ;
    throw Exception (ee.what ()) ;
  }
  is_busy = false ;

  LoadBitmapInfoHeader () ;

  PaintBox->SetBounds (0, 0, image.getWidth (), image.getHeight ()) ;
  MainForm->ProgressBar->Visible = false ;
  Cursor = crDefault ;
  return ;
}


void __fastcall TMDIChild::PaintBoxPaint(TObject *Sender)
{
  if (image_type == Colosseum::UnknownImage)
    return ;

  SetDIBitsToDevice (PaintBox->Canvas->Handle,
                     0,
                     0,
                     image.getWidth (),
                     image.getHeight (),
                     0,
                     0,
                     0,
                     image.getHeight (),
                     &image [0],
                     reinterpret_cast<BITMAPINFO *>(&image_header),
                     DIB_RGB_COLORS) ;
  return ;
}
//---------------------------------------------------------------------------


void TMDIChild::LoadBitmapInfoHeader ()
{
  image_header.bV4Size = sizeof (BITMAPV4HEADER) ;
  image_header.bV4Width = image.getWidth () ;
  image_header.bV4Height = - image.getHeight () ;
  image_header.bV4Planes = 1 ;
  image_header.bV4BitCount = 32 ;
  image_header.bV4V4Compression = BI_BITFIELDS ;
  image_header.bV4SizeImage = 0 ;
  image_header.bV4XPelsPerMeter = 0 ;
  image_header.bV4YPelsPerMeter = 0 ;
  image_header.bV4ClrUsed = 0 ;
  image_header.bV4ClrImportant = 0 ;
  image_header.bV4BlueMask = 0xFFU ;
  image_header.bV4GreenMask = 0xFF00U ;
  image_header.bV4RedMask = 0xFF0000U ;
  image_header.bV4AlphaMask = 0xFF000000U ;
  return ;
}

void __fastcall TMDIChild::SetGammaValue (double value)
{
  double adjust = value / gamma_value ;
  Cursor = crHourGlass ;
  Application->ProcessMessages () ;
  image.GammaCorrect (adjust) ;
  gamma_value = value ;
  LoadBitmapInfoHeader () ;
  PaintBox->Refresh () ;
  Cursor = crDefault ;
  return ;
}

void TMDIChild::Grayscale ()
{
  Cursor = crHourGlass ;
  Application->ProcessMessages () ;
  image.ToGrayscale () ;
  LoadBitmapInfoHeader () ;
  PaintBox->Refresh () ;
  Cursor = crDefault ;
  return ;
}

void TMDIChild::CopyToClipboard ()
{
  // The clipboard format for a Device Independent Bitmap
  // is a BITMAPINFOHEADER followed by the image data.
  unsigned int headersize = sizeof (BITMAPV4HEADER) ;
  unsigned int datasize = sizeof (BitmapImage::Pixel) * image.getWidth () * image.getHeight () ;

  // Allocate a global memory block for the iamge.
  HANDLE hmem = GlobalAlloc (GMEM_MOVEABLE, headersize + datasize) ;
  if (hmem == NULL)
    throw Exception ("Cannot Allocate Global Memory for Clipboard") ;
  char *data = (char *) GlobalLock (hmem) ;

  // We have already created a BITMAPINFOHEADER structure for the
  // image in order to display it. Here we copy that structure into
  // the buffer followed by the image data.
  memcpy (data, &image_header, headersize) ;
  memcpy (&data [headersize], &image [0], datasize) ;

  // Now we write the image to the clipboard. This transfers ownership
  // of the global block to the system.
  bool status = OpenClipboard (Application->Handle) ;
  if (! status)
  {
    GlobalFree (hmem) ;
    throw Exception ("Cannot Open the Clipboard") ;
  }
  HANDLE clipboard = SetClipboardData (CF_DIB, hmem) ;
  CloseClipboard () ;
  // Cleanup.
  GlobalUnlock (hmem) ;
  return ;
}

void TMDIChild::CopyFromClipboard ()
{
  OpenClipboard (Application->Handle) ;
  HANDLE hmem = GetClipboardData (CF_DIB) ;
  if (hmem == NULL)
  {
    CloseClipboard () ;
    return ;
  }

  char *buffer = (char *) GlobalLock (hmem) ;
  BITMAPINFOHEADER *header = (BITMAPINFOHEADER *) buffer ;

  unsigned int colorcount ;
  if (header->biBitCount < 16)
    colorcount = 1 << header->biBitCount ;
  else
    colorcount = 0 ;
  image.setSize (header->biWidth,
                 header->biHeight > 0 ? header->biHeight : - header->biHeight) ;
  BITMAPINFO *info = (BITMAPINFO *) buffer ;
  char *imagedata = &buffer [header->biSize
                             + colorcount * sizeof (RGBQUAD)] ;
  unsigned int datasize = sizeof (BitmapImage::Pixel) * image.getWidth () * image.getHeight () ;
  memcpy (&image [0], imagedata, datasize) ;
  CloseClipboard () ;
  GlobalFree (hmem) ;
  LoadBitmapInfoHeader () ;
  image_type = Colosseum::BmpImage ;
  PaintBox->SetBounds (0, 0, image.getWidth (), image.getHeight ()) ;
  Caption = "Untitled" ;
  return ;
}

void TMDIChild::SetAlpha (unsigned int value)
{
  BitmapImage::Pixel *pixel = &image [0] ;
  for (unsigned int ii = 0 ; ii < image.getWidth () * image.getHeight () ; ++ ii)
    pixel [ii].alpha = value ;
  return ;
}

void TMDIChild::MergeImage (const String &filename)
{
  is_busy = true ;

  MainForm->ShowProgressBar () ;
  Cursor = crHourGlass ;
  image_type = Colosseum::UnknownImage ;
  BitmapImage newimage ;
  try
  {
    image_type = Colosseum::ReadImage (
                             filename.c_str (),
                             newimage,
                             ProgressFunction,
                             reinterpret_cast<void*>(this)) ;
  }
  catch (std::exception &ee)
  {
    Caption = "" ;
    Cursor = crDefault ;
    MainForm->ProgressBar->Visible = false ;
    is_busy = false ;
    throw Exception (ee.what ()) ;
  }
  is_busy = false ;

  image.mergeImage (newimage) ;
  LoadBitmapInfoHeader () ;

  PaintBox->SetBounds (0, 0, image.getWidth (), image.getHeight ()) ;
  MainForm->ProgressBar->Visible = false ;
  PaintBox->Invalidate () ;
  Cursor = crDefault ;
  return ;
}

