/*
 *  dfiasco.c:          Decoder public interface
 *
 *  Written by:         Ullrich Hafner
 *
 *  This file is part of FIASCO (Fractal Image And Sequence COdec)
 *  Copyright (C) 1994-2000 Ullrich Hafner
 */

/*
 *  $Date: 2000/10/28 17:39:30 $
 *  $Author: hafner $
 *  $Revision: 5.7 $
 *  $State: Exp $
 */

#include <stdlib.h>
#include <string.h>

#include "pm_c_util.h"
#include "nstring.h"

#include "config.h"

#include "types.h"
#include "macros.h"
#include "error.h"

#include "dfiasco.h"
#include "wfa.h"
#include "read.h"
#include "misc.h"
#include "bit-io.h"
#include "decoder.h"
#include "options.h"
#include "wfalib.h"

/*****************************************************************************

                                prototypes

*****************************************************************************/

static dfiasco_t *
cast_dfiasco (fiasco_decoder_t *dfiasco);
static void
free_dfiasco (dfiasco_t *dfiasco);
static dfiasco_t *
alloc_dfiasco (wfa_t *wfa, video_t *video, bitfile_t *input,
               int enlarge_factor, int smoothing, format_e image_format);

/*****************************************************************************

                                public code

*****************************************************************************/

fiasco_decoder_t *
fiasco_decoder_new (const char *filename, const fiasco_d_options_t *options)
{
   try
   {
      bitfile_t          *input;        /* pointer to WFA FIASCO stream */
      wfa_t              *wfa;          /* wfa structure */
      video_t            *video;        /* information about decoder state */
      const d_options_t  *dop;          /* decoder additional options */
      dfiasco_t          *dfiasco;      /* decoder internal state */
      fiasco_decoder_t   *decoder;      /* public interface to decoder */
      fiasco_d_options_t *default_options = NULL;

      if (options)
      {
         dop = cast_d_options ((fiasco_d_options_t *) options);
         if (!dop)
            return NULL;
      }
      else
      {
         default_options = fiasco_d_options_new ();
         dop             = cast_d_options (default_options);
      }

      wfa   = alloc_wfa (NO);
      video = alloc_video (NO);
      input = open_wfa (filename, wfa->wfainfo);
      read_basis (wfa->wfainfo->basis_name, wfa);

      decoder              = Calloc (1, sizeof (fiasco_decoder_t));
      decoder->delete      = fiasco_decoder_delete;
      decoder->write_frame = fiasco_decoder_write_frame;
      decoder->get_frame   = fiasco_decoder_get_frame;
      decoder->get_length  = fiasco_decoder_get_length;
      decoder->get_rate    = fiasco_decoder_get_rate;
      decoder->get_width   = fiasco_decoder_get_width;
      decoder->get_height  = fiasco_decoder_get_height;
      decoder->get_title   = fiasco_decoder_get_title;
      decoder->get_comment = fiasco_decoder_get_comment;
      decoder->is_color    = fiasco_decoder_is_color;

      decoder->private = dfiasco
                       = alloc_dfiasco (wfa, video, input,
                                        dop->magnification,
                                        dop->smoothing,
                                        dop->image_format);

      if (default_options)
         fiasco_d_options_delete (default_options);
      if (dfiasco->enlarge_factor >= 0)
      {
         int           n;
         unsigned long pixels = wfa->wfainfo->width * wfa->wfainfo->height;

         for (n = 1; n <= (int) dfiasco->enlarge_factor; n++)
         {
            if (pixels << (n << 1) > 2048 * 2048)
            {
               set_error (_("Magnifaction factor `%d' is too large. "
                            "Maximum value is %d."),
                          dfiasco->enlarge_factor, MAX(0, n - 1));
               fiasco_decoder_delete (decoder);
               return NULL;
            }
         }
      }
      else
      {
         int n;

         for (n = 0; n <= (int) - dfiasco->enlarge_factor; n++)
         {
            if (wfa->wfainfo->width >> n < 32
                || wfa->wfainfo->height >> n < 32)
            {
               set_error (_("Magnifaction factor `%d' is too small. "
                            "Minimum value is %d."),
                          dfiasco->enlarge_factor, - MAX(0, n - 1));
               fiasco_decoder_delete (decoder);
               return NULL;
            }
         }
      }
      return (fiasco_decoder_t *) decoder;
   }
   catch
   {
      return NULL;
   }
}

int
fiasco_decoder_write_frame (fiasco_decoder_t *decoder,
                            const char *filename)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return 0;
   else
   {
      try
      {
         image_t *frame = get_next_frame (NO, dfiasco->enlarge_factor,
                                          dfiasco->smoothing, NULL,
                                          FORMAT_4_4_4, dfiasco->video, NULL,
                                          dfiasco->wfa, dfiasco->input);
         write_image (filename, frame);
      }
      catch
      {
         return 0;
      }
      return 1;
   }
}

fiasco_image_t *
fiasco_decoder_get_frame (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return NULL;
   else
   {
      try
      {
         fiasco_image_t *image = Calloc (1, sizeof (fiasco_image_t));
         image_t        *frame = get_next_frame (NO, dfiasco->enlarge_factor,
                                                 dfiasco->smoothing, NULL,
                                                 dfiasco->image_format,
                                                 dfiasco->video, NULL,
                                                 dfiasco->wfa, dfiasco->input);

         frame->reference_count++;      /* for motion compensation */
         image->private    = frame;
         image->delete     = fiasco_image_delete;
         image->get_width  = fiasco_image_get_width;
         image->get_height = fiasco_image_get_height;
         image->is_color   = fiasco_image_is_color;

         return image;
      }
      catch
      {
         return NULL;
      }
   }
}

unsigned
fiasco_decoder_get_length (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return 0;
   else
      return dfiasco->wfa->wfainfo->frames;
}

unsigned
fiasco_decoder_get_rate (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return 0;
   else
      return dfiasco->wfa->wfainfo->fps;
}

unsigned
fiasco_decoder_get_width (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return 0;
   else
   {
      unsigned width;

      if (dfiasco->enlarge_factor >= 0)
         width = dfiasco->wfa->wfainfo->width << dfiasco->enlarge_factor;
      else
         width = dfiasco->wfa->wfainfo->width >> - dfiasco->enlarge_factor;

      return width & 1 ? width + 1 : width;
   }
}

unsigned
fiasco_decoder_get_height (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return 0;
   else
   {
      unsigned height;

      if (dfiasco->enlarge_factor >= 0)
         height = dfiasco->wfa->wfainfo->height << dfiasco->enlarge_factor;
      else
         height = dfiasco->wfa->wfainfo->height >> - dfiasco->enlarge_factor;

      return height & 1 ? height + 1 : height;
   }
}

const char *
fiasco_decoder_get_title (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return NULL;
   else
      return dfiasco->wfa->wfainfo->title;
}

const char *
fiasco_decoder_get_comment (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return NULL;
   else
      return dfiasco->wfa->wfainfo->comment;
}

int
fiasco_decoder_is_color (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return 0;
   else
      return dfiasco->wfa->wfainfo->color;
}

int
fiasco_decoder_delete (fiasco_decoder_t *decoder)
{
   dfiasco_t *dfiasco = cast_dfiasco (decoder);

   if (!dfiasco)
      return 1;

   try
   {
      free_wfa (dfiasco->wfa);
      free_video (dfiasco->video);
      close_bitfile (dfiasco->input);
      strcpy (dfiasco->id, " ");
      free_dfiasco(dfiasco);
      Free (decoder);
   }
   catch
   {
      return 0;
   }

   return 1;
}

/*****************************************************************************

                                private code

*****************************************************************************/

static dfiasco_t *
alloc_dfiasco (wfa_t *wfa, video_t *video, bitfile_t *input,
               int enlarge_factor, int smoothing, format_e image_format)
/*
 *  FIASCO decoder constructor:
 *  Initialize decoder structure.
 *
 *  Return value:
 *      pointer to the new decoder structure
 */
{
   dfiasco_t *dfiasco = Calloc (1, sizeof (dfiasco_t));

   strcpy (dfiasco->id, "DFIASCO");

   dfiasco->wfa            = wfa;
   dfiasco->video          = video;
   dfiasco->input          = input;
   dfiasco->enlarge_factor = enlarge_factor;
   dfiasco->smoothing      = smoothing;
   dfiasco->image_format   = image_format;

   return dfiasco;
}

static void
free_dfiasco (dfiasco_t *dfiasco)
/*
 *  FIASCO decoder destructor:
 *  Free memory of given 'decoder' struct.
 *
 *  No return value.
 *
 *  Side effects:
 *      'video' struct is discarded.
 */
{
   Free (dfiasco);
}

static dfiasco_t *
cast_dfiasco (fiasco_decoder_t *dfiasco)
/*
 *  Cast pointer `dfiasco' to type dfiasco_t.
 *  Check whether `dfiasco' is a valid object of type dfiasco_t.
 *
 *  Return value:
 *      pointer to dfiasco_t struct on success
 *      NULL otherwise
 */
{
   dfiasco_t *this = (dfiasco_t *) dfiasco->private;
   if (this)
   {
      if (!streq (this->id, "DFIASCO"))
      {
         set_error (_("Parameter `dfiasco' doesn't match required type."));
         return NULL;
      }
   }
   else
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "dfiasco");
   }

   return this;
}
