/*
 * wclipbrd.c -- Clipboard routines for WinBoard
 * $Id: wclipbrd.c,v 1.4 2000-05-03 18:12:00-07 default Exp default $
 *
 * Copyright 2000 Free Software Foundation, Inc.
 *
 * ------------------------------------------------------------------------
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * ------------------------------------------------------------------------
 */

#include "config.h"

#include <windows.h>   /* required for all Windows applications */
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <io.h>
#include <assert.h>

#include "common.h"
#include "winboard.h"
#include "frontend.h"
#include "backend.h"
#include "wclipbrd.h"

#ifdef _DEBUG
int _CrtCheckMemory( void );
#endif

/* Imports from winboard.c */
extern HWND hwndMain;

VOID 
CopyFENToClipboard()
{
  char *fen = NULL;

  fen = PositionToFEN(currentMove);
  if (!fen) {
    DisplayError("Unable to convert position to FEN.", 0);
    return;
  }
  if (!CopyTextToClipboard(fen))
      DisplayError("Unable to copy FEN to clipboard.", 0);
  free(fen);
}


VOID
CopyGameToClipboard()
{
  /* A rather cheesy hack here. Write the game to a file, then read from the
   * file into the clipboard.  Can't use a temp file for this, since SaveGame()
   * closes the file.
   *
   * The alternative is to create separate routines in backend.c which will
   * save a game directly to a dynamically sized memory buffer rather than a
   * file.  Using a file is much simpler and avoids duplicating the logic
   * in SaveGame() and PrintPGNTags(), etc.
   */
  char *buf = NULL;
  char *fname = NULL;
  FILE *f;
  unsigned long size;
  size_t len;

#ifdef _DEBUG
    assert(_CrtCheckMemory()); // check heap is still valid
#endif

    fname = tmpnam(NULL);
  if (!fname) {
      DisplayError("Cannot create temporary file name.",0);
      return;
  }
  f = fopen(fname, "w"); 
  if (!f) {
    DisplayError("Cannot open temporary file.", 0);
    return;
  }
  if (!SaveGame(f,0,"")) { 			/* call into backend */
    DisplayError("Cannot reopen temporary file.", 0);
    goto copy_game_to_clipboard_cleanup;
  }
  f = fopen(fname, "rb");
  if (!f) {
    DisplayError("Cannot reopen temporary file.", 0);
    goto copy_game_to_clipboard_cleanup;
  }
  size = filelength(fileno(f));
  if (size == -1) {
    DisplayError("Cannot determine size of file.", 0);
    goto copy_game_to_clipboard_cleanup;
  }
#ifdef _DEBUG
    assert(_CrtCheckMemory()); // check heap is still valid
#endif
  rewind(f);
  buf = (char*)malloc(size+1);
  if (!buf) {
    DisplayError("Cannot allocate clipboard buffer.", 0);
    goto copy_game_to_clipboard_cleanup;
  }
  len = fread(buf, sizeof(char), size, f);
  if (len == -1) {
    DisplayError("Cannot read from temporary file.", 0);
    goto copy_game_to_clipboard_cleanup;
  }
#if 1
  fprintf(debugFP, "CopyGameToClipboard(): size %ld, len %u\n", size, len);
#endif
  if ((unsigned long)size != (unsigned long)len) { /* sigh */ 
      DisplayError("Error reading from temporary file.", 0);
      goto copy_game_to_clipboard_cleanup;
  }
  buf[size] = 0;
  if (!CopyTextToClipboard(buf)) {
      DisplayError("Cannot copy text to clipboard", 0);
  }

copy_game_to_clipboard_cleanup:
  if (buf) free(buf);
  if (f) fclose(f);
  remove(fname);

//  if (fname) free(fname);
}


int 
CopyTextToClipboard(char *text)
{


  /* some (most?) of the error checking may be overkill, 
   * but hey, this is Windows 
   */
  HGLOBAL hGlobalMem;
  LPVOID lpGlobalMem;
  BOOL locked;
  UINT lockCount;
  DWORD err;

#ifdef _DEBUG
    assert(_CrtCheckMemory()); // check heap is still valid
#endif

    hGlobalMem = GlobalAlloc(GHND, (DWORD)lstrlen(text)+1);
  if (hGlobalMem == NULL) {
    DisplayError("Unable to allocate memory for clipboard.", 0);
    return FALSE;
  }
  lpGlobalMem = GlobalLock(hGlobalMem);
  if (lpGlobalMem == NULL) {
    DisplayError("Unable to lock clipboard memory.", 0);
    GlobalFree(hGlobalMem);
    return FALSE;
  }
  lstrcpy(lpGlobalMem, text);
  if (appData.debugMode) {
    lockCount = GlobalFlags(hGlobalMem) & GMEM_LOCKCOUNT;
    fprintf(debugFP, "CopyTextToClipboard(): lock count %d\n", lockCount);
  }
  SetLastError(NO_ERROR);
  locked = GlobalUnlock(hGlobalMem);
  err = GetLastError();
  if (appData.debugMode) {
    lockCount = GlobalFlags(hGlobalMem) & GMEM_LOCKCOUNT;
    fprintf(debugFP, "CopyTextToClipboard(): lock count %d\n", lockCount);
  }
  if (!locked) {
    locked = !((err == NO_ERROR) || (err == ERROR_NOT_LOCKED));
    if (appData.debugMode) {
      fprintf(debugFP, 
	      "CopyTextToClipboard(): err %d locked %d\n", err, locked);
    }
  }
  if (locked) {
    DisplayError("Cannot unlock clipboard memory.", 0);
    GlobalFree(hGlobalMem);
    return FALSE;
  }
  if (!OpenClipboard(hwndMain)) {
    DisplayError("Cannot open clipboard.", 0);
    GlobalFree(hGlobalMem);
    return FALSE;
  }
  if (!EmptyClipboard()) {
    DisplayError("Cannot empty clipboard.", 0);
    return FALSE;
  }
  if (hGlobalMem != SetClipboardData(CF_TEXT, hGlobalMem)) {
    DisplayError("Cannot copy text to clipboard.", 0);
    CloseClipboard();
    GlobalFree(hGlobalMem);
    return FALSE;
  }
  if (!CloseClipboard())
    DisplayError("Cannot close clipboard.", 0);
#ifdef _DEBUG
    assert(_CrtCheckMemory()); // check heap is still valid
#endif
  
  return TRUE;
}



VOID
PasteFENFromClipboard()
{
  char *fen = NULL;
  if (!PasteTextFromClipboard(&fen)) {
      DisplayError("Unable to paste FEN from clipboard.", 0);
      return;
  }
  if (appData.debugMode) {
    fprintf(debugFP, "PasteFenFromClipboard(): fen '%s'\n", fen);
  }
  EditPositionPasteFEN(fen); /* call into backend */
  free(fen);
}


VOID
PasteGameFromClipboard()
{
  /* Another rather cheesy hack here.
   * Write the clipboard to a temp file, then let LoadGame() do all the work.
   *
   * The alternative is to create routines very similar to LoadGame() and
   * LoadGameOneMove() which operate on a char* rather than a FILE*. This just
   * seems like too much duplication to me. Using a temp file with the existing
   * functionality is much simpler. 
   *
   * The only downside occurs when the clipboard does not contain a valid game
   * score. In that case the user will get a somewhat confusing error message
   * indicating that no game was found in the "file".
   */
  char *buf;
  FILE *f;
  size_t len;
  if (!PasteTextFromClipboard(&buf)) {
    return;
  }
  f = tmpfile();
  if (!f) {
    DisplayError("Unable to create temporary file.", 0);
    return;
  }
  len = fwrite(buf, sizeof(char), strlen(buf), f);
  if (len != strlen(buf)) {
    DisplayError("Error writing to temporary file.", 0);
    fclose(f);
    return;
  }
  LoadGame(f,1,"Clipboard",FALSE);	/* call into backend */
  /* temp file will be deleted when app is closed */
}


int 
PasteTextFromClipboard(char **text)
{
  /* some (most?) of the error checking may be overkill, 
   * but hey, this is Windows 
   */
  HANDLE hClipMem;
  LPVOID lpClipMem;
  BOOL locked=FALSE;
  DWORD err;
  UINT lockCount;

  if (!OpenClipboard(hwndMain)) {
    DisplayError("Unable to open clipboard.", 0);
    return FALSE;
  }
  hClipMem = GetClipboardData(CF_TEXT);
  if (hClipMem == NULL) {
    CloseClipboard();
    DisplayError("No text in clipboard.", 0);
    return FALSE;
  }
  lpClipMem = GlobalLock(hClipMem);
  if (lpClipMem == NULL) {
    CloseClipboard();
    DisplayError("Unable to lock clipboard memory.", 0);
    return FALSE;
  }
  *text = (char *) malloc(GlobalSize(hClipMem)+1);
  if (!*text) {
    DisplayError("Unable to allocate memory for text string.", 0);
    CloseClipboard();
    return FALSE;
  } 
  lstrcpy(*text, lpClipMem);
  if (appData.debugMode) {
    lockCount = GlobalFlags(hClipMem) & GMEM_LOCKCOUNT;
    fprintf(debugFP, "PasteTextFromClipboard(): lock count %d\n", lockCount);
  }
  SetLastError(NO_ERROR);
  lockCount = GlobalFlags(hClipMem) & GMEM_LOCKCOUNT;
  if (lockCount)
    locked = GlobalUnlock(hClipMem);
  err = GetLastError();
  if (appData.debugMode) {
    lockCount = GlobalFlags(hClipMem) & GMEM_LOCKCOUNT;
    fprintf(debugFP, "PasteTextFromClipboard(): lock count %d\n", lockCount);
  }
  if (!locked) {
    locked = !((err == NO_ERROR) || (err == ERROR_NOT_LOCKED));
    if (appData.debugMode) {
      fprintf(debugFP, 
	      "PasteTextFromClipboard(): err %d locked %d\n", err, locked);
    }
  }
  if (locked) 
    DisplayError("Unable to unlock clipboard memory.", 0);
  
  if (!CloseClipboard())
    DisplayError("Unable to close clipboard.", 0);
  
  return TRUE;
}
