/* bluebox distributor
 * version 0.3.2 date 2007-07-18
 * Copyright (C) 2006-2007 Stefan Schuermans <stefan@blinkenarea.org>
 * Copyleft: GNU public license V2.0 - http://www.gnu.org/copyleft/gpl.html
 * a BlinkenArea project - http://www.blinkenarea.org/
 */

#include <stdio.h>
#include <malloc.h>

#include "bd_config.h"
#include "bd_fmt.h"

//load a format file
//returns pointer to malloc-ed st_bd_fmt structure on success, NULL on error
st_bd_fmt * bd_fmt_load( char * filename )
{
  FILE * p_file;
  st_bd_fmt * p_bd_fmt;
  int cnt, newline;
  unsigned short x, y;
  char buffer[16];

  //check filename (fopen segaults in these cases)
  if( filename == NULL || filename[0] == 0 )
  {
    printf( "bd_fmt: no format file was supplied\n" );
    return NULL;
  }

  //open file
  p_file = fopen( filename, "rt" );
  if( p_file == NULL )
  {
    printf( "bd_fmt: could not open format file \"%s\"\n", filename );
    return NULL;
  }

  //allocate structure
  p_bd_fmt = (st_bd_fmt *)malloc( sizeof( st_bd_fmt ) );
  if( p_bd_fmt == NULL )
  {
    fclose( p_file );
    printf( "bd_fmt: could not allocate format structure\n" );
    return NULL;
  }

  //read width and height
  fscanf( p_file, "%*[ \t\r]" );
  cnt = fscanf( p_file, "%hu%*[ \t\r]%hu", &p_bd_fmt->width, &p_bd_fmt->height );
  fscanf( p_file, "%*[ \t\r]" );
  newline = fgetc( p_file ) == '\n';
  if( cnt != 2 || ! newline )
  {
    free( p_bd_fmt );
    fclose( p_file );
    printf( "bd_fmt: could not read dimensions (\"<width> <height>\")\n" );
    return NULL;
  }
  if( p_bd_fmt->width < 1 || p_bd_fmt->width > 1000 || p_bd_fmt->height < 1 || p_bd_fmt->height > 1000 )
  {
    free( p_bd_fmt );
    fclose( p_file );
    printf( "bd_fmt: invalid dimensions (\"%d %d\")\n", p_bd_fmt->width, p_bd_fmt->height );
    return NULL;
  }

  //allocate two-dimensional array for format pixels
  p_bd_fmt->pixels = (st_bd_fmt_pix * *)malloc( p_bd_fmt->height * sizeof( st_bd_fmt_pix * ) );
  if( p_bd_fmt->pixels == NULL )
  {
    free( p_bd_fmt );
    fclose( p_file );
    printf( "bd_fmt: could not allocate pixel buffer for format structure\n" );
    return NULL;
  }
  for( y = 0; y < p_bd_fmt->height; y++ )
  {
    p_bd_fmt->pixels[y] = (st_bd_fmt_pix *)malloc( p_bd_fmt->width * sizeof( st_bd_fmt_pix ) );
    if( p_bd_fmt->pixels[y] == NULL )
    {
      for( y-- ; (short)y >= 0; y-- )
        free( p_bd_fmt->pixels[y] );
      free( p_bd_fmt->pixels );
      free( p_bd_fmt );
      fclose( p_file );
      printf( "bd_fmt: could not allocate pixel buffer for format structure\n" );
      return NULL;
    }
  }

  //read format pixels
  for( y = 0; y < p_bd_fmt->height; y++ )
  {
    for( x = 0; x < p_bd_fmt->width; x++ )
    {
      //read in whitespace
      cnt = fscanf( p_file, "%1[ \t\r]%*[ \t\r]", buffer );
      if( x > 0 && cnt != 1 ) //there must be whitespace if not in the first column
      {
        printf( "bd_fmt: missing whitespace before pixel in column %d of row %d\n", x, y );
        break;
      }
      //pixel is invalid by default
      p_bd_fmt->pixels[y][x].valid = 0;
      //check if ignore pixel
      cnt = fscanf( p_file, "%1[.,/-]", buffer );
      //it is an ignore pixel
      if( cnt == 1 )
      {
        //mark pixel as invalid
        p_bd_fmt->pixels[y][x].dev_no = -1;
        p_bd_fmt->pixels[y][x].ser_no = -1;
        p_bd_fmt->pixels[y][x].pix_no = -1;
        p_bd_fmt->pixels[y][x].out_buf_ofs = -1;
        p_bd_fmt->pixels[y][x].sync_bit = 0x80;
        p_bd_fmt->pixels[y][x].valid = 0;
      }
      //it is not an ignore pixel
      else
      {
        //read pixel
        cnt = fscanf( p_file,
                      "%hu%*[.,/-]%hu%*[.,/-]%hu",
                      &p_bd_fmt->pixels[y][x].dev_no,
                      &p_bd_fmt->pixels[y][x].ser_no,
                      &p_bd_fmt->pixels[y][x].pix_no );
        //success
        if( cnt == 3 )
        {
          //check values
          if( p_bd_fmt->pixels[y][x].dev_no >= bd_out_dev_cnt
           || p_bd_fmt->pixels[y][x].ser_no >= bd_out_ser_cnt
           || p_bd_fmt->pixels[y][x].pix_no >= bd_out_pix_cnt )
          {
            printf( "bd_fmt: pixel in column %d of row %d (\"%d-%d-%d\") does not exist\n",
                    x, y,
                    p_bd_fmt->pixels[y][x].dev_no,
                    p_bd_fmt->pixels[y][x].ser_no,
                    p_bd_fmt->pixels[y][x].pix_no );
            break;
          }
          //calculate offset into output buffer
          p_bd_fmt->pixels[y][x].out_buf_ofs = (1 + p_bd_fmt->pixels[y][x].pix_no) * bd_out_ser_cnt
                                             + p_bd_fmt->pixels[y][x].ser_no;
          //save sync bit
          p_bd_fmt->pixels[y][x].sync_bit = p_bd_fmt->pixels[y][x].pix_no == 0 ? 0x80 : 0x00;
          //mark pixel as valid
          p_bd_fmt->pixels[y][x].valid = 1;
        }
        else
        {
          printf( "bd_fmt: pixel in column %d of row %d has invalid format\n", x, y );
          break;
        }
      } //it is not an ignore pixel
    } //for( x ...
    //end outer loop if error during inner loop
    if( x < p_bd_fmt->width )
      break;
    //read newline
    fscanf( p_file, "%*[ \t\r]" );
    newline = fgetc( p_file ) == '\n';
    if( ! newline )
    {
      if( ! feof( p_file ) )
        printf( "bd_fmt: too much pixels specified for row %d\n", y );
      else
        printf( "bd_fmt: too few rows specified\n" );
      break;
    }
  } //for( y ...
  //error found during loop
  if( y < p_bd_fmt->height )
  {
    for( y = 0; y < p_bd_fmt->height; y++ )
      free( p_bd_fmt->pixels[y] );
    free( p_bd_fmt->pixels );
    free( p_bd_fmt );
    return NULL;
  }

  //close file
  fclose( p_file );

  return p_bd_fmt;
}

//free a format
void bd_fmt_free( st_bd_fmt * p_bd_fmt )
{
  unsigned short y;

  //free two-dimensional array with format pixels
  for( y = 0; y < p_bd_fmt->height; y++ )
    free( p_bd_fmt->pixels[y] );
  free( p_bd_fmt->pixels );

  //free structure
  free( p_bd_fmt );
}
