//
// Copyright (C) 1999-2002 Toshikaz Hirabayashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// TOSHIKAZ HIRABAYASHI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Except as contained in this notice, the name of Toshikaz Hirabayashi shall
// not be used in advertising or otherwise to promote the sale, use or other
// dealings in this Software without prior written authorization from
// Toshikaz Hirabayashi.

#include <WScom.h>
#include <win/WSDwinImage.h>
#include <WSCimageSet.h>
#include <WSClocaleSet.h>
#include <WSDfileSystem.h>
#include <win/WSDwinAppDev.h>
#include <win/WSDwinColor.h>

extern "C" {
#include <jpeglib.h>
#include <png.h>
};
extern HBITMAP JpegReadFileToBitmap(char* filename,WSCulong* w,WSCulong* h);
extern HBITMAP PngReadFileToBitmap(char* filename,WSCulong* w,WSCulong* h);

WSMFclassInit(WSDwinImage,WSDimage);

WSDimage* _win_image_create_handler(){
  WSDimage* image = new WSDwinImage();
  return image;
};


void _win_image_init(){
    WSGIappImageSet()->setCreateHandler(_win_image_create_handler);
};

WSDwinImage::WSDwinImage(){
  hBitmap = NULL;
  nColors = 0;
}

WSDwinImage::~WSDwinImage(){
  destroy();
}

long WSDwinImage::initialize(){
  char* iname = getSrc();
  if (iname == NULL || !strcmp(iname,"") ){
    return WS_NO_ERR;
  }
  setValue1(-1);
  setValue2(-1);

  WSCstring in;
  in.setString(WSGIappFileSystem()->adjustFileName( iname ));
  while( in.replaceString("/","\\",0) );

  if (in.isExist(".jpg") != -1 || in.isExist(".JPG") != -1){
    WSCulong w,h;
    hBitmap = JpegReadFileToBitmap((char*)in,&w,&h);
    if (hBitmap != 0){
      setImageWidth(w);
      setImageHeight(h);
      setValue1((long)hBitmap);
      setValue2((long)0);
      return WS_NO_ERR;
    }
  }
  if (in.isExist(".png") != -1 || in.isExist(".PNG") != -1){
    WSCulong w,h;
    hBitmap = PngReadFileToBitmap((char*)in,&w,&h);
    if (hBitmap != 0){
      setImageWidth(w);
      setImageHeight(h);
      setValue1((long)hBitmap);
      setValue2((long)0);
      return WS_NO_ERR;
    }
  }


  in.replaceString(".xpm", ".bmp", 0);
  in.replaceString(".XPM", ".bmp", 0);
  in.replaceString(".jpg", ".bmp", 0);
  in.replaceString(".JPG", ".bmp", 0);
  in.replaceString(".png", ".bmp", 0);
  in.replaceString(".PNG", ".bmp", 0);
  char* load_file_name = in.getString(WSGIappLocaleSet()->
                                      getSystemLocaleEncoding());
 // hBitmap = (HBITMAP) ::LoadImage( WSGIwinAppDev()->getInstanceHandle(), 
  hBitmap = (HBITMAP)LoadImage( NULL,
       (LPCTSTR)load_file_name,
       IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
WSMFtrace("XXZZim1 WSDwinImage::init fname=#%s# 0x%x \n",(char*)in,hBitmap);
  if (hBitmap == 0){
    long words = in.getWords("\\");
    if (words > 1){
      WSCstring in2 = in.getWord(words-1,"\\");
      load_file_name = (char*)in2;
      hBitmap = (HBITMAP)LoadImage( NULL,
        (LPCTSTR)load_file_name,
        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
    }
  }
  if (hBitmap == 0){
    WSCstring in2(iname);
    in2.replaceString(".xpm", ".bmp", 0);
    in2.replaceString(".XPM", ".bmp", 0);
    in2.replaceString(".jpg", ".bmp", 0);
    in2.replaceString(".JPG", ".bmp", 0);
    long val = (long)WSGIappImageSet()->getDefaultImageHandle((char*)in2);
    if (val != 0){
      hBitmap = (HBITMAP)LoadBitmap(WSGIwinAppDev()->getInstanceHandle(), (char*)val);
    }
  }
  if (hBitmap == 0){
    in.replaceString(".bmp", ".ico", 0);
    in.replaceString(".BMP", ".ico", 0);
    load_file_name = (char*)in;
    hBitmap = (HBITMAP)LoadImage( NULL,
        (LPCTSTR)load_file_name,
        IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
    if (hBitmap == 0){
      long words = in.getWords("\\");
      if (words > 1){
        WSCstring in2 = in.getWord(words-1,"\\");
        load_file_name = (char*)in2;
        hBitmap = (HBITMAP)LoadImage( NULL,
          (LPCTSTR)load_file_name,
          IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_LOADREALSIZE );
      }
    }
    if (hBitmap == 0){
      WSCstring in2(iname);
      in2.replaceString(".xpm", ".ico", 0);
      in2.replaceString(".XPM", ".ico", 0);
      in2.replaceString(".jpg", ".ico", 0);
      in2.replaceString(".JPG", ".ico", 0);
      long val = (long)WSGIappImageSet()->getDefaultImageHandle((char*)in2);
      if (val != 0){
        hBitmap = (HBITMAP)LoadIcon(WSGIwinAppDev()->getInstanceHandle(), (char*)val);
      }
WSMFtrace("XXZZim1 WSDwinImage::load val=%d #%s# hBitmap=0x%x hInst=0x%x done.\n",val,(char*)in2,hBitmap,WSGIwinAppDev()->getInstanceHandle());
    }
    if (hBitmap ==0) {
      return WS_ERR;
    }
    setImageWidth(32);
    setImageHeight(32);
    setValue1((long)hBitmap);
    setValue2((long)1);
    return WS_NO_ERR;
  }



  if (hBitmap !=0) {
    DIBSECTION ds;
//    SIZE size1;
    GetObject(hBitmap,sizeof(DIBSECTION),&ds);
    setImageWidth(ds.dsBm.bmWidth);
    setImageHeight(ds.dsBm.bmHeight);
    setValue1((long)hBitmap);
    setValue2((long)0);
WSMFtrace("WSDwinImage::init fname=#%s# 0x%x   %d,%d\n",(char*)in,hBitmap, ds.dsBm.bmWidth, ds.dsBm.bmHeight);

  }

   return WS_NO_ERR;
}

long WSDwinImage::destroy(){
  if (hBitmap != 0){
    DeleteObject((void*)hBitmap);
    hBitmap = 0;
  }
  setValue1(-1);
  setValue2(-1);
  return WS_NO_ERR;
}


static void ehandler(j_common_ptr jinf,int level){
}

static void exhandler(j_common_ptr jinf){
}
static int chandler(j_decompress_ptr jinf){
return TRUE;
}

HBITMAP JpegReadFileToBitmap(char* filename,WSCulong* w,WSCulong* h){

    int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;
//    int start_shift, msb_flag;
//    unsigned int start_mask;
    BITMAPINFO bmi;
    HDC mdc;
    long bytes_per_line;

    red_mask = green_mask = blue_mask = 0;
    red_shift = green_shift = blue_shift = 0;
    WSCstring fname(filename);
    
    FILE* ifile = fopen(fname.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()),
                        "rb");
    if (ifile == NULL){
      return NULL;
    }
    struct jpeg_decompress_struct    jinf;
    struct jpeg_error_mgr            jerr;

    jinf.err = jpeg_std_error(&jerr);
    jinf.err->emit_message = ehandler;
    jinf.err->error_exit = exhandler;

    jpeg_create_decompress(&jinf);
    jpeg_set_marker_processor(&jinf,JPEG_COM,chandler);

    jpeg_stdio_src(&jinf,ifile);

WSMFtrace("1 cinf->global_state=%d\n",jinf.global_state);
    jpeg_read_header(&jinf,TRUE);
    jpeg_calc_output_dimensions(&jinf);
WSMFtrace("2 cinf->global_state=%d\n",jinf.global_state);
    jpeg_start_decompress(&jinf);

    long width = jinf.output_width;
    long height = jinf.output_height;
    *w = width;
    *h = height;

    long components = jinf.output_components;

    unsigned char* buf = new unsigned char[components * height * width*sizeof(JSAMPLE)];

    JSAMPROW rowptr[1];
    long i;
    for(i=0; i< height; i++){
      rowptr[0] = (JSAMPROW)&(buf[i * width * components]);
      jpeg_read_scanlines(&jinf, rowptr,1);
    }

    bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth=width;
    bmi.bmiHeader.biHeight=height;
    bmi.bmiHeader.biPlanes=1;
    bmi.bmiHeader.biBitCount=24;
    bmi.bmiHeader.biCompression=BI_RGB;
    bmi.bmiHeader.biSizeImage=0;
    bmi.bmiHeader.biXPelsPerMeter=0;
    bmi.bmiHeader.biYPelsPerMeter=0;
    bmi.bmiHeader.biClrUsed=0;
    bmi.bmiHeader.biClrImportant=0;

//    long gap = width*3 + bytes_per_line;
    bytes_per_line = (width+1)*3 & ~3;

    WSCuchar* data = new WSCuchar[bytes_per_line * height];

#if 0
    short tmp1 = 0xff00;
    char* tmp2 = (char*)&tmp1; 
    if (tmp2[0] == 0xff){
      msb_flag = 1;
    }else{
      msb_flag = 0;
    }
#endif

    unsigned char r,g,b;
    long ptr = 0;
    long ptr2 = 0;
    long j;

    if (components == 3){
WSMFtrace("XXZZim1 here3\n");
//        long end = height*width-1;
        for(i=0; i<height; i++){
          for(j=0; j<width; j++){
            long base = (height - 1 -i)*bytes_per_line + j*3;
            ((unsigned char*)data)[base +2] = buf[ptr++];
            ((unsigned char*)data)[base +1] = buf[ptr++];
            ((unsigned char*)data)[base] = buf[ptr++];
            ptr2 +=3;
          }
        }
      }else{
WSMFtrace("XXZZim1 here1\n");
        for(i=0; i<height; i++){
          for(j=0; j<width; j++){
            r = buf[ptr]>>3;
            g = buf[ptr]>>2;
            b = buf[ptr++]>>3;
            ((short*)data)[ptr2++] =  r <<11 | g<<5 | b;
          }
        }
    }
    HDC hdc = GetDC((HWND)WSGIwinAppDev()->getWindowResource());
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc,width,height);
    ReleaseDC((HWND)WSGIwinAppDev()->getWindowResource(),hdc);

    mdc = CreateCompatibleDC(NULL);
    if (!SetDIBits(mdc,hBitmap,0,height,data,&bmi,DIB_RGB_COLORS)){
WSMFtrace("XXZZim1 setDIBits error %s\n",filename);
      DeleteObject((void*)hBitmap);
      hBitmap = 0;
    }
    GdiFlush();
    DeleteDC(mdc);

    jpeg_finish_decompress(&jinf);
    jpeg_destroy_decompress(&jinf);

    if (data != NULL){
      delete data;
      data = NULL;
    }
    if (buf != NULL){
      delete buf;
    }

    fclose(ifile);
    return hBitmap;
}

void png_cexcept_error(png_structp png_ptr, png_const_charp msg){
   if(png_ptr){
     fprintf(stderr, "png file read error: %s\n", msg);
   }
}

HBITMAP PngReadFileToBitmap(char* filename,WSCulong* w,WSCulong* h){

    int red_mask, green_mask, blue_mask;
    int red_shift, green_shift, blue_shift;
//    int start_shift, msb_flag;
//    unsigned int start_mask, udat;
    BITMAPINFO bmi;
    HDC mdc;
    long bytes_per_line;

    red_mask = green_mask = blue_mask = 0;
    red_shift = green_shift = blue_shift = 0;

    WSCstring fname(filename);
    
    FILE* ifile = fopen(fname.getString(WSGIappLocaleSet()->getSystemLocaleEncoding()),
                        "rb");
    if (ifile == NULL){
//printf("XXZZ Png err1 #%s#\n",(char*)fname);
      return NULL;
    }

    png_byte            sig[8];
    fread(sig, 1, 8, ifile);
    if (!png_check_sig(sig, 8)){
//printf("XXZZ Png err2\n");
      return NULL;
    }
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
      (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL);
    if (png_ptr == NULL){
//printf("XXZZ Png err3\n");
      return NULL;
    } 
    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL){
//printf("XXZZ Png err4\n");
      png_destroy_read_struct(&png_ptr, NULL, NULL);
      return NULL;
    }

    png_init_io(png_ptr, ifile);
    png_set_sig_bytes(png_ptr, 8);
    png_read_info(png_ptr, info_ptr);
    png_uint_32 png_width;
    png_uint_32 png_height;
    int png_depth;
    int png_color_type;
    png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &png_depth,
            &png_color_type, NULL, NULL, NULL);

    if (png_depth == 16){
      png_set_strip_16(png_ptr);
    }
    if (png_color_type == PNG_COLOR_TYPE_PALETTE){
      png_set_expand(png_ptr);
    }
    if (png_depth < 8){
      png_set_expand(png_ptr);
    }
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)){
      png_set_expand(png_ptr);
    }
    if (png_color_type == PNG_COLOR_TYPE_GRAY ||
        png_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){
      png_set_gray_to_rgb(png_ptr);
    }

    png_uint_32 png_row_bytes = png_get_rowbytes(png_ptr, info_ptr);
    png_uint_32 png_channels = png_get_channels(png_ptr, info_ptr);

    long rwidth = png_width;
    long rheight = png_height;
    long components = png_channels;

    unsigned char* buf =
        new unsigned char[png_row_bytes * png_height * sizeof(png_byte)];
    if (buf == NULL){
fprintf(stderr,"png read error: out of memory..\n");
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
//printf("XXZZ Png err5\n");
      return NULL;
    }
    png_byte** png_row_ptrs = new png_bytep[png_height];
    if (png_row_ptrs == NULL){
fprintf(stderr,"png read error: out of memory..\n");
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
//printf("XXZZ Png err6\n");
      return NULL;
    }
    int i;
    for(i = 0; i < (int)png_height; i++){
      png_row_ptrs[i] = (png_byte*)(buf + i * png_row_bytes);
    }
    png_read_image(png_ptr, png_row_ptrs);
    png_read_end(png_ptr,NULL);
    delete png_row_ptrs;

    *w = rwidth;
    *h = rheight;

    long width = (long)rwidth;
    long height = (long)rheight;

    bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth=width;
    bmi.bmiHeader.biHeight=height;
    bmi.bmiHeader.biPlanes=1;
    bmi.bmiHeader.biBitCount=24;
    bmi.bmiHeader.biCompression=BI_RGB;
    bmi.bmiHeader.biSizeImage=0;
    bmi.bmiHeader.biXPelsPerMeter=0;
    bmi.bmiHeader.biYPelsPerMeter=0;
    bmi.bmiHeader.biClrUsed=0;
    bmi.bmiHeader.biClrImportant=0;

    bytes_per_line = (width+1)*3 & ~3;

    WSCuchar* data = new WSCuchar[bytes_per_line * height];

    unsigned char r,g,b;
    long ptr = 0;
    long ptr2 = 0;
    long j;

    if (components == 3){
        for(i=0; i<height; i++){
          for(j=0; j<width; j++){
            long base = (height - 1 -i)*bytes_per_line + j*3;
            ((unsigned char*)data)[base +2] = buf[ptr++];
            ((unsigned char*)data)[base +1] = buf[ptr++];
            ((unsigned char*)data)[base] = buf[ptr++];
            ptr2 +=3;
          }
        }
      }else{
        for(i=0; i<height; i++){
          for(j=0; j<width; j++){
            r = buf[ptr]>>3;
            g = buf[ptr]>>2;
            b = buf[ptr++]>>3;
            ((short*)data)[ptr2++] =  r <<11 | g<<5 | b;
          }
        }
    }
    HDC hdc = GetDC((HWND)WSGIwinAppDev()->getWindowResource());
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc,width,height);
    ReleaseDC((HWND)WSGIwinAppDev()->getWindowResource(),hdc);

    mdc = CreateCompatibleDC(NULL);
    if (!SetDIBits(mdc,hBitmap,0,height,data,&bmi,DIB_RGB_COLORS)){
      DeleteObject((void*)hBitmap);
      hBitmap = 0;
    }
    GdiFlush();
    DeleteDC(mdc);


    if (data != NULL){
      delete data;
      data = NULL;
    }
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    if (buf != NULL){
      delete buf;
    }

//printf("XXZZ Png return OK\n");

    fclose(ifile);
    return hBitmap;
}
