/***************************************************************************
                          cdtext.c  -  description
                             -------------------
    begin                : Mon Feb 12 2001
    copyright            : (C) 2001,2003 by Alex Kern
    email                : alex.kern@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef libunicode
  #include <unicode.h>
#endif

#include "include/wm_config.h"
#include "include/wm_struct.h"
#include "include/wm_cdrom.h"
#include "include/wm_platform.h"
#include "include/wm_helpers.h"
#include "include/wm_cdtext.h"
#include "include/wm_scsi.h"

#define WM_MSG_CLASS WM_MSG_CLASS_MISC

/* local prototypes */
int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock);
int free_cdtext_info(struct cdtext_info* cdtextinfo);
struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks);
void get_data_from_cdtext_pack(
  const struct cdtext_pack_data_header *pack,
  cdtext_string *p_componente);

struct cdtext_info wm_cdtext_info;

int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock)
{
  if(cdtextinfoblock)
  {
    if(cdtextinfoblock->name)
      free(cdtextinfoblock->name);
    if(cdtextinfoblock->performer)
      free(cdtextinfoblock->performer);
    if(cdtextinfoblock->songwriter)
      free(cdtextinfoblock->songwriter);
    if(cdtextinfoblock->composer)
      free(cdtextinfoblock->composer);
    if(cdtextinfoblock->arranger)
      free(cdtextinfoblock->arranger);
    if(cdtextinfoblock->message)
      free(cdtextinfoblock->message);
    if(cdtextinfoblock->UPC_EAN_ISRC_code)
      free(cdtextinfoblock->UPC_EAN_ISRC_code);

    if(cdtextinfoblock->block_encoding_text)
      free(cdtextinfoblock->block_encoding_text);
  }

  return 0;
}

int free_cdtext_info(struct cdtext_info* cdtextinfo)
{
  int i;
  wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
    "CDTEXT INFO: free_cdtext_info() called\n");

  if(cdtextinfo)
  {
    for(i = 0; i < 8; i++)
    {
      if(cdtextinfo->blocks[i])
      {
        free_cdtext_info_block(cdtextinfo->blocks[i]);
      }
    }
    memset(cdtextinfo, 0, sizeof(struct cdtext_info));
  }

  return 0;
}

struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks)
{
  int memamount;
  struct cdtext_info_block* lp_block;

  lp_block = malloc(sizeof(struct cdtext_info_block));
  if(!lp_block)
  {
    return (struct cdtext_info_block*)0;
  }
  memset(lp_block, 0, sizeof(struct cdtext_info_block));

  memamount = count_of_tracks * sizeof(cdtext_string);

  lp_block->name = malloc(memamount);
  if(!lp_block->name)
    goto bail_out;
  memset(lp_block->name, 0, memamount);

  lp_block->performer = malloc(memamount);
  if(!lp_block->performer)
    goto bail_out;
  memset(lp_block->performer, 0, memamount);

  lp_block->songwriter = malloc(memamount);
  if(!lp_block->songwriter)
    goto bail_out;
  memset(lp_block->songwriter, 0, memamount);

  lp_block->composer = malloc(memamount);
  if(!lp_block->composer)
    goto bail_out;
  memset(lp_block->composer, 0, memamount);

  lp_block->arranger = malloc(memamount);
  if(!lp_block->arranger)
    goto bail_out;
  memset(lp_block->arranger, 0, memamount);

  lp_block->message = malloc(memamount);
  if(!lp_block->message)
    goto bail_out;
  memset(lp_block->message, 0, memamount);

  lp_block->UPC_EAN_ISRC_code = malloc(memamount);
  if(!lp_block->UPC_EAN_ISRC_code)
    goto bail_out;
  memset(lp_block->UPC_EAN_ISRC_code, 0, memamount);

  return lp_block;

bail_out:
  free_cdtext_info_block(lp_block);
  free(lp_block);
  return 0;
}

void get_data_from_cdtext_pack(
  const struct cdtext_pack_data_header *pack,
  cdtext_string *p_componente)
{

  int arr = pack->header_field_id2_tracknumber;
  int i;
  int language_block;
  int unicode;

  language_block = (pack->header_field_id4_block_no >> 4) & 0x07;
  unicode = pack->header_field_id4_block_no & 0x80;

  if(!unicode)
  {
    for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i++)
    {
      if(pack->text_data_field[i] == 0x00) /* end marker */
      {
        arr++;
      }
      else if(pack->text_data_field[i] == 0x09) /* repeat last marker */
      {
        /* ASSERT(arr > 0) */
        strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1]));
        arr++;
      }
      else
      {
      	strncat((char*)(p_componente[arr]), (char*)(&(pack->text_data_field[i])), 1);
      }
    }
  }
#ifdef libunicode
  else /* doublebytes ;-) */
  {
    for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i += 2)
    {
      if((Uchar)(pack->text_data_field[i]) == 0x0000) /* end marker */
      {
        arr++;
      }
      else if((Uchar)(pack->text_data_field[i]) == 0x0909) /* repeat last marker */
      {
        /* ASSERT(arr > 0) */
        strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1]));
        arr++;
      }
      else
      {
        strncat((char*)(p_componente[arr]), u_uc_to_utf8((Uchar*)(&(pack->text_data_field[i]))), 1);
      }
    }
  }
#else
  else {
     wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS, "cannot handle unicode");
  }
#endif
}

struct cdtext_info *get_glob_cdtext(struct wm_drive *d, int redo)
{
  /* alloc cdtext_info */

  unsigned char *buffer;
  int buffer_length;
  int ret;
  int i;
  struct cdtext_pack_data_header *pack, *pack_previous;
  cdtext_string *p_componente;
  struct cdtext_info_block *lp_block;

  if(!redo && wm_cdtext_info.valid) {
    wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, "CDTEXT DEBUG: recycle cdtext\n");
    return &wm_cdtext_info;
  } else {
    free_cdtext_info(&wm_cdtext_info);
  }

  lp_block = 0;
  p_componente = 0;
  buffer = 0;
  buffer_length = 0;

  ret = wm_scsi_get_cdtext(d, &buffer, &buffer_length);
  if(!ret)
  {
    if(!d->proto.get_trackcount || d->proto.get_trackcount(d, &wm_cdtext_info.count_of_entries) < 0)
      wm_cdtext_info.count_of_entries = 1;
    else
      wm_cdtext_info.count_of_entries++;

    i = 0;

    pack = 0; /* NULL pointer*/
    while(i < buffer_length)
    {
      pack_previous = pack;
      pack = (struct cdtext_pack_data_header*)(buffer+i);
      /* to implement: check_crc(pack); */

      /* only for valid packs */
      if(pack->header_field_id1_typ_of_pack >= 0x80 && pack->header_field_id1_typ_of_pack < 0x90)
      {
        int code, j;
          wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
            "CDTEXT DEBUG: valid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
            i,
            pack->header_field_id1_typ_of_pack,
            pack->header_field_id2_tracknumber,
            pack->header_field_id3_sequence,
            pack->header_field_id4_block_no,
            pack->text_data_field[0],
            pack->text_data_field[1],
            pack->text_data_field[2],
            pack->text_data_field[3],
            pack->text_data_field[4],
            pack->text_data_field[5],
            pack->text_data_field[6],
            pack->text_data_field[7],
            pack->text_data_field[8],
            pack->text_data_field[9],
            pack->text_data_field[10],
            pack->text_data_field[11],
            pack->crc_byte1,
            pack->crc_byte2);
        wm_cdtext_info.count_of_valid_packs++;

        code = (pack->header_field_id4_block_no >> 4) & 0x07;
        if(0 == lp_block || lp_block->block_code != code) /* find or create one new block */
        {
          lp_block = 0;
          for(j = 0; j < MAX_LANGUAGE_BLOCKS && wm_cdtext_info.blocks[j] != 0 && 0 == lp_block; j++)
          {
            if(wm_cdtext_info.blocks[j]->block_code == code)
            {
              lp_block = wm_cdtext_info.blocks[j];
            }
          }

          if(MAX_LANGUAGE_BLOCKS <= j)
          {
            free_cdtext_info(&wm_cdtext_info);
            wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS,
              "CDTEXT ERROR: more as 8 languageblocks defined\n");
            return NULL;
          }

          if(0 == lp_block)
          {
            /* make next new block */
            lp_block = malloc_cdtext_info_block(wm_cdtext_info.count_of_entries);
            if(0 == lp_block)
            {
              wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS,
                "CDTEXT ERROR: out of memory, cannot create a new language block\n");
              free_cdtext_info(&wm_cdtext_info);
              return NULL /*ENOMEM*/;
            }
            else
            {
              wm_cdtext_info.blocks[j] = lp_block;
              wm_cdtext_info.blocks[j]->block_code = code;
              wm_cdtext_info.blocks[j]->block_unicode = pack->header_field_id4_block_no & 0x80;
              wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
                "CDTEXT INFO: created a new language block; code %i, %s characters\n", code, lp_block->block_unicode?"doublebyte":"singlebyte");
/*
  unsigned char block_encoding; not jet!
  cdtext_string* block_encoding_text;
*/
            }
          }
        }
      }

      switch(pack->header_field_id1_typ_of_pack)
      {
        case 0x80:
          p_componente = (lp_block->name);
          get_data_from_cdtext_pack(pack, p_componente);
          break;
        case 0x81:
          p_componente = (lp_block->performer);
          get_data_from_cdtext_pack(pack, p_componente);
          break;
        case 0x82:
          p_componente = (lp_block->songwriter);
          get_data_from_cdtext_pack(pack, p_componente);
          break;
        case 0x83:
          p_componente = (lp_block->composer);
          get_data_from_cdtext_pack(pack, p_componente);
          break;
        case 0x84:
          p_componente = (lp_block->arranger);
          get_data_from_cdtext_pack(pack, p_componente);
          break;
        case 0x85:
          p_componente = (lp_block->message);
          get_data_from_cdtext_pack(pack, p_componente);
          break;
        case 0x86:
          memcpy((char*)(lp_block->binary_disc_identification_info),
          (char*)(pack->text_data_field),  DATAFIELD_LENGHT_IN_PACK);
          break;
        case 0x87:
          memcpy((char*)(lp_block->binary_genreidentification_info),
          (char*)(pack->text_data_field),  DATAFIELD_LENGHT_IN_PACK);
          break;
        case 0x88:
         wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
           "CDTEXT INFO: PACK with code 0x88 (TOC)\n");
          break;
        case 0x89:
         wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
           "CDTEXT INFO: PACK with code 0x89 (second TOC)\n");
          break;
        case 0x8A:
        case 0x8B:
        case 0x8C:
          wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
            "CDTEXT INFO: PACK with code 0x%02X (reserved)\n", pack->header_field_id1_typ_of_pack);
          break;
        case 0x8D:
          wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
            "CDTEXT INFO: PACK with code 0x8D (for content provider only)\n");
          break;
        case 0x8E:
          p_componente = (lp_block->UPC_EAN_ISRC_code);
          get_data_from_cdtext_pack(pack, p_componente);
          break;
        case 0x8F:
          memcpy((char*)(lp_block->binary_size_information),
          (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
          break;
        default:
          wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
            "CDTEXT ERROR: invalid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
            i,
            pack->header_field_id1_typ_of_pack,
            pack->header_field_id2_tracknumber,
            pack->header_field_id3_sequence,
            pack->header_field_id4_block_no,
            pack->text_data_field[0],
            pack->text_data_field[1],
            pack->text_data_field[2],
            pack->text_data_field[3],
            pack->text_data_field[4],
            pack->text_data_field[5],
            pack->text_data_field[6],
            pack->text_data_field[7],
            pack->text_data_field[8],
            pack->text_data_field[9],
            pack->text_data_field[10],
            pack->text_data_field[11],
            pack->crc_byte1,
            pack->crc_byte2);
          wm_cdtext_info.count_of_invalid_packs++;
      }
      i += sizeof(struct cdtext_pack_data_header);
    } /* while */
  }

  if(0 == ret && wm_cdtext_info.count_of_valid_packs > 0)
    wm_cdtext_info.valid = 1;

  return &wm_cdtext_info;
}

void free_cdtext(void)
{
  if (wm_cdtext_info.valid)
    free_cdtext_info(&wm_cdtext_info);
}
