/*
PURPOSE
Generic parser module to read byte by byte from the serial input
and change to the corresponding state so that it can parse and 
return command packets to the Process function

INPUT PARAMETERS
void* payloadBfr - pointer sent by the Process function

RETURN VALUES
0  - On Successful parsing of a command

*/
void ParsePkt(void* payloadBfr)
{
    ParserState parserState = P1;               /* Initialise parser state */
    ParserErrors parserErrors = PREAMBLE1_ERR;  /* To store the Error Number */
    INT16S c = 0;                               /* Variable to read a byte */
    INT16U i = 0;                               /* Variable to use in loop */ 
    INT8U csVal = 0;                            /* check sum in the command */ 
    
    PktBfr *pktBuf = (PktBfr *)payloadBfr;
    for(;;) {
        c = InByte();

        switch(parserState) {
            case P1:        /* Preamble State 1 */
                if(c == PREAMBLE_BYTE1)
                    parserState = P2;                
                else
                    SetErrorState(&parserState, &parserErrors, PREAMBLE1_ERR);  
                break;
            case P2:        /* Preamble State 2 */
                if(c == PREAMBLE_BYTE2)
                    parserState = P3;
                else
                    SetErrorState(&parserState, &parserErrors, PREAMBLE2_ERR);
                break;
            case P3:        /* Preamble State 3 */
                if(c == PREAMBLE_BYTE3)
                    parserState = C;
                else
                    SetErrorState(&parserState, &parserErrors, PREAMBLE3_ERR);
                break;               
            case C:         /* Check sum state */                
                 csVal = SetState(NULL, &parserState, NULL, c);   
                break;
            case L:         /* Length State */
                if(c >= LEN_CUTOFF) {
                    parserState = T;
                    pktBuf->dataLen = (INT8U)(c - LEN_CUTOFF);
                }
                else
                    SetErrorState(&parserState, &parserErrors, LENGTH_ERR);                     break;
            case T:        /* Packet Type State */                
                pktBuf->pktType = (INT8U)c;		/* Store the Packet Type */
                if(!SetState(pktBuf,&parserState, &parserErrors, csVal)) 
                    return;
                i = 0; 
                break;
            case D:           /* Packet Data State */
                pktBuf->data[i++] = (INT8U)c;             
                if(i >= pktBuf->dataLen)
                    if(!SetState(pktBuf,&parserState,&parserErrors,csVal)) 
                        return;
                break;
            case ER:          /* Error State */
                if(c == PREAMBLE_BYTE1)   /* go to P2 only if preamble 1 */
                    parserState = P2;
                break;
        }
    }
}
// CopyPaletteFrom()
//  Copies the given palette into the image's palette.
//  Only valid in Indexed mode.  The from palette must
//  be an array of IMG_BYTES consisting of RGB tripls,
//  just like the ImageClass's palette.  The from palette
//  must also contain at least as many color indices as
//  this image class instance.
bool ImageClass::CopyPaletteFrom( const IMG_BYTE * from ) {
  if( type != IMAGE_INDEXED ) {
    SetErrorState( IMAGE_WRONG_MODE_FOR_FUNCTION );
    return false;
  }

  if( from == NULL )
    return false;

  if( from == palette )
    return true;

  memcpy( palette, from, num_registers * sizeof( IMG_BYTE ) * 3 );
  return true;
}
// GetRGBAt( point )
//  Return the RGB color at the given point.
//  Invalid in Greyscale mode
const IMG_BYTE * ImageClass::GetRGBAt( int point ) {
  int index;

  if( type == IMAGE_INDEXED ) {         // Indexed Image
    index = image[ GetIndexAt( point ) ];
    return GetPaletteColor( index );
  } else if( type == IMAGE_GREY ) {     // Grey; Invalid
    SetErrorState( IMAGE_WRONG_MODE_FOR_FUNCTION );
    return NULL;
  } else if( (type == IMAGE_RGBA) || ( type = IMAGE_RGB ) ) {  // True-Color Image
    return &( image[ GetIndexAt( point ) ] );
  }

  // Unknown; return 0
  return 0;
}
/*
PURPOSE
Single function to set the parser state and error state. This is to reduce the parser functions length 

INPUT PARAMETERS
PktBfr *pktBuf - The packet buffer
ParserState *parserState - The parser state
ParserErrors *parserErrors - Errors
INT8U csVal - checksum value

RETURN VALUES
0 -  Only when the checksum is correct
1 - Otherwise

*/
INT8U SetState(PktBfr *pktBuf,ParserState *parserState, ParserErrors *parserErrors, INT8U csVal)
{
    /* If state is checksum set next state as Length and return */
    if(*parserState == C)
    {
        *parserState = L;
        return(csVal);
    }
    
    if(*parserState == T && pktBuf->dataLen != 0)
        *parserState = D;
    else{
        /* Need to calculate checksum */
        if(CheckSum(pktBuf) == csVal) {
            *parserState = P1;
            return(0);
        }
        else 
            SetErrorState(parserState,parserErrors, CHECKSUM_ERR);
    }
    return(1);
}
/**
 * Update the various fields of a MythNotificationScreen.
 */
void MythNotificationScreen::Init(void)
{
    if (!m_refresh) // nothing got changed so far, return
        return;

    AdjustYPosition();

    if (m_artworkImage && (m_update & kImage))
    {
        if (!m_imagePath.isNull())
        {
            // We have a path to the image, use it
            m_artworkImage->SetFilename(m_imagePath);
            m_artworkImage->Load();
        }
        else if (!m_image.isNull())
        {
            // We don't have a path to the image, but the image itself
            MythImage *img = m_artworkImage->GetPainter()->GetFormatImage();
            img->Assign(m_image);
            m_artworkImage->SetImage(img);
            img->DecrRef();
        }
        else
        {
            // Will default to displaying whatever placeholder image is defined
            // in the xml by the themer, means we can show _something_ rather than
            // a big empty hole. Generally you always want to call Reset() in
            // these circumstances
            m_artworkImage->Reset();
        }
    }

    if (m_update != kNone)
    {
        InfoMap tmap;

        tmap["title"]               = m_title;
        if (m_update & kImage)
        {
            tmap["image"]           = m_imagePath;
        }
        tmap["origin"]              = m_origin;
        tmap["description"]         = m_description;
        tmap["extra"]               = m_extra;
        if (m_update & kDuration)
        {
            tmap["progress_text"]   = m_progresstext;
            tmap["progress"]        = QString("%1").arg((int)(m_progress * 100));
        }
        SetTextFromMap(tmap);
    }

    if (m_update & kMetaData)
    {
        if (m_titleText && m_title.isNull())
        {
            m_titleText->Reset();
        }
        if (m_originText && m_origin.isNull())
        {
            m_originText->Reset();
        }
        if (m_descriptionText && m_description.isNull())
        {
            m_descriptionText->Reset();
        }
        if (m_extraText && m_extra.isNull())
        {
            m_extraText->Reset();
        }
    }

    if (m_update & kDuration)
    {
        if (m_progresstextText && m_progresstext.isEmpty())
        {
            m_progresstextText->Reset();
        }
        if (m_progressBar)
        {
            if (m_progress >= 0)
            {
                m_progressBar->SetStart(0);
                m_progressBar->SetTotal(100);
                m_progressBar->SetUsed(100 * m_progress);
            }
            else
            {
                // Same as above, calling Reset() allows for a sane, themer defined
                //default to be displayed
                m_progressBar->Reset();
            }
        }
    }

    if (m_progressBar)
    {
        m_progressBar->SetVisible((m_content & kDuration) != 0);

    }

    SetErrorState();

    if (m_mediaState && (m_update & kImage))
    {
        m_mediaState->DisplayState(m_update & kNoArtwork ? "noartwork" : "ok");
        LOG(VB_GUI, LOG_DEBUG, LOC + QString("Init: Set media state to %1").arg(m_update & kNoArtwork ? "noartwork" : "ok"));
    }

    // No field will be refreshed the next time unless specified otherwise
    m_update = kNone;

    if (GetScreenStack() && !m_added)
    {
        GetScreenStack()->AddScreen(this);
        m_added = true;
    }
    m_refresh = false;
}
bool MythNotificationScreen::Create(void)
{
    bool foundtheme = false;

    // Load the theme for this screen
    // The xml file containing the screen definition is airplay-ui.xml in this
    // example, the name of the screen in the xml is airplaypicture. This
    // should make sense when you look at the xml below

    QString theme;
    if (m_fullscreen)
    {
        theme = "notification-full";
    }
    else if (m_content & kImage)
    {
        theme = "notification-image";
    }
    else
    {
        theme = "notification";
    }

    QString theme_attempt = theme + (m_style.isEmpty() ? "" : "-" + m_style);

    // See if we have an alternative theme available as defined in the notification
    foundtheme = LoadWindowFromXML("notification-ui.xml", theme_attempt, this);
    if (!foundtheme && theme_attempt != theme)
    {
        // if not, default to the main one
        foundtheme = LoadWindowFromXML("notification-ui.xml", theme, this);
    }

    if (!foundtheme) // If we cannot load the theme for any reason ...
        return false;

    m_artworkImage      = dynamic_cast<MythUIImage*>(GetChild("image"));
    m_titleText         = dynamic_cast<MythUIText*>(GetChild("title"));
    m_originText        = dynamic_cast<MythUIText*>(GetChild("origin"));
    m_descriptionText   = dynamic_cast<MythUIText*>(GetChild("description"));
    m_extraText         = dynamic_cast<MythUIText*>(GetChild("extra"));
    m_progresstextText  = dynamic_cast<MythUIText*>(GetChild("progress_text"));
    m_progressBar       = dynamic_cast<MythUIProgressBar*>(GetChild("progress"));
    m_errorState        = dynamic_cast<MythUIStateType*>(GetChild("errorstate"));
    m_mediaState        = dynamic_cast<MythUIStateType*>(GetChild("mediastate"));

    SetErrorState();

    if (m_mediaState && (m_update & kImage))
    {
        m_mediaState->DisplayState(m_content & kNoArtwork ? "noartwork" : "ok");
        LOG(VB_GUI, LOG_DEBUG, LOC + QString("Create: Set media state to %1").arg(m_content & kNoArtwork ? "noartwork" : "ok"));
    }

    // store original position
    m_position      = GetPosition();
    m_created = true;

    if ((m_visibility & ~MythNotification::kPlayback) == 0)
    {
        // Visibility will be set automatically during video playback
        // so can be ignored here
        SetVisible(false);
    }

    // We need to re-run init
    m_refresh = true;

    return true;
}
// ConvertTypeTo()
//  Converts the image from it's current type to the new type.  This
//   is NOT an in-place operation; rather, it creates and returns a
//   new ImageClass, which must be freed by the main program.  The
//   original ImageClass is not modified in any way.  For conversion
//   to indexed images, an optional palette can also be passed in.
//   Images will be quantized to match the palette as needed.
//   This function is most useful for image savers and device-dependant
//   output of images.
//  Note that if the source image doesn't need to be converted, this
//   function will return a pointer to the existing image class, so
//   be sure to check for this.  A returned value of NULL means some
//   error occured
ImageClass * ImageClass::ConvertTypeTo( int new_type,
                                        int num_reg, IMG_BYTE *pal ) {
  if( new_type == IMAGE_INVALID_TYPE )      // Invalid type; fail
    return NULL;

  if( new_type == type )                   // Already in the new mode; return this image
    return this;

  ImageClass * new_image = NULL;
  int x, y;
  IMG_BYTE       *new_pal;
  const IMG_BYTE *rgb;
  IMG_BYTE        value;

  switch( new_type ) {
  case IMAGE_RGB:       // Convert to RGB
    new_image = new ImageClass( width, height, IMAGE_RGB );
    if( !new_image || !(*new_image) ) {
      SetErrorState( IMAGE_OUT_OF_RAM );
      delete new_image;
      return NULL;
    }

    for( y=0; y < height; y++ ) {
      for( x=0; x < width; x++ ) {
        rgb = GetRGBAt( x, y );
        new_image->SetRGBAt( x, y, rgb );
      }
    }

    return new_image;
    break;

  case IMAGE_RGBA:      // Convert to RGBA
    new_image = new ImageClass( width, height, IMAGE_RGBA );
    if( !new_image || !(*new_image) ) {
      SetErrorState( IMAGE_OUT_OF_RAM );
      delete new_image;
      return NULL;
    }

    for( y=0; y < height; y++ ) {
      for( x=0; x < width; x++ ) {
        rgb = GetRGBAt( x, y );
        new_image->SetRGBAt( x, y, rgb );
      }
    }
    new_image->ClearAlpha();

    return new_image;
    break;

  case IMAGE_GREY:      // Convert to Greyscale
    new_image = new ImageClass( width, height, IMAGE_GREY );
    if( !new_image || !(*new_image) ) {
      SetErrorState( IMAGE_OUT_OF_RAM );
      delete new_image;
      return NULL;
    }

    for( y=0; y < height; y++ ) {
      for( x=0; x < width; x++ ) {
        value = GetValueAt( x, y );
        new_image->SetValueAt( x, y, value );
      }
    }

    return new_image;
    break;
  
  case IMAGE_INDEXED:   // Convert to Indexed
    new_image = new ImageClass( width, height, IMAGE_INDEXED, num_reg );
    if( !new_image || !(*new_image) ) {
      SetErrorState( IMAGE_OUT_OF_RAM );
      delete new_image;
      return NULL;
    }

    if( pal != NULL )   // Copy the new palette in, if applicable
      new_image->CopyPaletteFrom( pal );
    else if ( type == IMAGE_GREY ) {  // Generate a greyscale palette
      for( int i=0; i < 256; i++ ) {
        new_image->SetPaletteColor( i, i, i, i );
      }
    } else {              // Auto-generate a palette, if needed.
      new_pal = GeneratePalette( num_reg );
      new_image->CopyPaletteFrom( new_pal );
      delete new_pal;
    }

    for( y=0; y < height; y++ ) {
      for( x=0; x < width; x++ ) {
        if( type == IMAGE_GREY )
          value = GetValueAt( x, y );
        else {
          rgb   = GetRGBAt( x, y );
          value = new_image->FindPaletteIndex( rgb, 0, num_registers-1 );  // low/high must be a char, hence the -1.
        }

        new_image->SetPaletteIndexAt( x, y, value );
      }
    }

    return new_image;
    break;
  
  default:
    return NULL;
  }

}