/* GStreamer
 * Copyright (C) 2013 CableLabs, Louisville, CO 80027
 * Copyright (C) 2015 Samsung Electronics Co., Ltd.
 *     @Author: Chengjun Wang <cjun.wang@samsung.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */


#ifndef __GST_CEA708_DEC_H__
#define __GST_CEA708_DEC_H__

#include <gst/gst.h>
#include <pango/pangocairo.h>

G_BEGIN_DECLS
/* from ATSC A/53 Part 4
 * DTVCC packets are 128 bytes MAX, length is only 6 bits, header is 2 bytes,
 * the last byte is flag-fill, that leaves 125 possible bytes of data to be
 * represented in 6 bits, hence the length encoding
 */
/* should never be more than 128 */
#define DTVCC_LENGTH       128
#define DTVCC_PKT_SIZE(sz_byte)  (((sz_byte) == 0) ? 127 : ((sz_byte)  * 2) -1)
#define CCTYPE_VALID_MASK  0x04
#define CCTYPE_TYPE_MASK   0x03
#define NUM_608_CCTYPES 2
/* CEA-708-B commands */
/* EndOfText */
#define CC_COMMAND_ETX 0x03
/* SetCurrentWindow0 */
#define CC_COMMAND_CW0 0x80
#define CC_COMMAND_CW1 0x81
#define CC_COMMAND_CW2 0x82
#define CC_COMMAND_CW3 0x83
#define CC_COMMAND_CW4 0x84
#define CC_COMMAND_CW5 0x85
#define CC_COMMAND_CW6 0x86
#define CC_COMMAND_CW7 0x87
/* ClearWindows */
#define CC_COMMAND_CLW 0x88
/* DisplayWindows */
#define CC_COMMAND_DSW 0x89
/* HideWindows */
#define CC_COMMAND_HDW 0x8A
/* ToggleWindows */
#define CC_COMMAND_TGW 0x8B
/* DeleteWindows */
#define CC_COMMAND_DLW 0x8C
/* Delay */
#define CC_COMMAND_DLY 0x8D
/* DelayCancel */
#define CC_COMMAND_DLC 0x8E
/* Reset */
#define CC_COMMAND_RST 0x8F
/* SetPenAttributes */
#define CC_COMMAND_SPA 0x90
/* SetPenColor */
#define CC_COMMAND_SPC 0x91
/* SetPenLocation */
#define CC_COMMAND_SPL 0x92
/* SetWindowAttributes */
#define CC_COMMAND_SWA 0x97
/* DefineWindow0 */
#define CC_COMMAND_DF0 0x98
#define CC_COMMAND_DF1 0x99
#define CC_COMMAND_DF2 0x9A
#define CC_COMMAND_DF3 0x9B
#define CC_COMMAND_DF4 0x9C
#define CC_COMMAND_DF5 0x9D
#define CC_COMMAND_DF6 0x9E
#define CC_COMMAND_DF7 0x9F
/* music note unicode */
#define CC_SPECIAL_CODE_MUSIC_NOTE    0x266a
#define CC_UTF8_MAX_LENGTH   6
#define CC_MAX_CODE_SET_SIZE   96
/* Per CEA-708 spec there may be 8 CC windows */
#define MAX_708_WINDOWS  8
/* Each 708 window contains a grid of character positions. These are the
  * max limits defined, but each window has a row/col count which is typically
  * smaller than the limits. Note this is just one window, not the entire screen.
  */
/* max row count */
#define WINDOW_MAX_ROWS 15
/* max column width */
#define WINDOW_MAX_COLS 42
/* The linebuffer contains text for 1 line pango text corresponding to 1 line of 708 text.
  * The linebuffer could be a lot larger than the window text because of required markup.
  * example <u> </u> for underline.
  * The size given is an estimate, to be changed if determined that a larger
  * buffer is needed
  */
#define LINEBUFFER_SIZE 1024
/* The screen width/height defined by 708 - not character units, these are
  * used only to determine the position of the anchor on the screen.
  */
#define SCREEN_WIDTH_16_9    209
#define SCREEN_HEIGHT_16_9   74
#define SCREEN_WIDTH_4_3    159
#define SCREEN_HEIGHT_4_3    74

/* raw bytes of "define window" command */
#define WIN_DEF_SIZE 6
/* The maximum size of a 708 window in character units. This is used to
  * calculate the position of windows based on window anchor positions.
  */
#define SCREEN_HEIGHT_708  15
#define SCREEN_WIDTH_708   32
/* cea708 minimum color list */
#define CEA708_COLOR_INVALID   0xFF
#define CEA708_COLOR_BLACK     0x00
#define CEA708_COLOR_WHITE     0x2A
#define CEA708_COLOR_RED       0x20
#define CEA708_COLOR_GREEN     0x08
#define CEA708_COLOR_BLUE      0x02
#define CEA708_COLOR_YELLOW    0x28
#define CEA708_COLOR_MAGENTA   0x22
#define CEA708_COLOR_CYAN      0x0A
#define CEA708_PANGO_SPAN_MARKUP_START                      "<span"
#define CEA708_PANGO_SPAN_MARKUP_END                        "</span>"
#define CEA708_PANGO_SPAN_ATTRIBUTES_UNDERLINE_SINGLE       " underline='single'"
#define CEA708_PANGO_SPAN_ATTRIBUTES_STYLE_ITALIC           " style='italic'"
#define CEA708_PANGO_SPAN_ATTRIBUTES_FONT                   " font_desc="
#define CEA708_PANGO_SPAN_ATTRIBUTES_FOREGROUND             " foreground="
#define CEA708_PANGO_SPAN_ATTRIBUTES_BACKGROUND             " background="
#define MINIMUM_OUTLINE_OFFSET 1.0
#define WINDOW_IN_LIST_IS_ACTIVE(list) (list & 0x1)
typedef struct _Cea708Dec Cea708Dec;

typedef enum
{
  COLOR_TYPE_BLACK = 0,
  COLOR_TYPE_WHITE,
  COLOR_TYPE_RED,
  COLOR_TYPE_GREEN,
  COLOR_TYPE_BLUE,
  COLOR_TYPE_YELLOW,
  COLOR_TYPE_MAGENTA,
  COLOR_TYPE_CYAN,
  COLOR_TYPE_RESEVER
} Cea708ColorType;

typedef enum
{
  NO_CHANGE = 0,
  SWITCH_TO_HIDE,
  SWITCH_TO_SHOW,
  TOGGLE
} VisibilityControl;

typedef enum
{
  SOLID = 0,
  FLASH,
  TRANSLUCENT,
  TRANSPARENT
} Opacity;

typedef enum
{
  WIN_STYLE_NORMAL = 1,
  WIN_STYLE_TRANSPARENT,
  WIN_STYLE_NORMAL_CENTERED,
  WIN_STYLE_NORMAL_WORD_WRAP,
  WIN_STYLE_TRANSPARENT_WORD_WRAP,
  WIN_STYLE_TRANSPARENT_CENTERED,
  WIN_STYLE_ROTATED
} WindowStyle;

typedef enum
{
  PEN_STYLE_DEFAULT = 1,
  PEN_STYLE_MONO_SERIF,
  PEN_STYLE_PROP_SERIF,
  PEN_STYLE_MONO_SANS,
  PEN_STYLE_PROP_SANS,
  PEN_STYLE_MONO_SANS_TRANSPARENT,
  PEN_STYLE_PROP_SANS_TRANSPARENT
} PenStyle;

typedef enum
{
  ANCHOR_PT_TOP_LEFT = 0,
  ANCHOR_PT_TOP_CENTER,
  ANCHOR_PT_TOP_RIGHT,
  ANCHOR_PT_MIDDLE_LEFT,
  ANCHOR_PT_CENTER,
  ANCHOR_PT_MIDDLE_RIGHT,
  ANCHOR_PT_BOTTOM_LEFT,
  ANCHOR_PT_BOTTOM_CENTER,
  ANCHOR_PT_BOTTOM_RIGHT,
} AnchorPoint;

typedef enum
{
  TAG_DIALOG = 0,
  TAG_SPEAKER_ID,
  TAG_ELECTRONIC_VOICE,
  TAG_ALT_LANGUAGE_DIALOG,
  TAG_VOICEOVER,
  TAG_AUDIBLE_TRANSLATION,
  TAG_SUBTITLE_TRANSLATION,
  TAG_VOICE_QUALITY_DESCRIPTION,
  TAG_SONG_LYRICS,
  TAG_SOUND_EFFECT_DESCRIPTION,
  TAG_MUSICAL_SCORE_DESCRIPTION,
  TAG_EXPLETIVE,
  TAG_UNDEF1,
  TAG_UNDEF2,
  TAG_UNDEF3,
  TAG_NOT_DISPLAYED
} TagType;

typedef enum
{
  JUSTIFY_LEFT = 0,
  JUSTIFY_RIGHT,
  JUSTIFY_CENTER,
  JUSTIFY_FULL
} JUSTIFY_MODE;

typedef enum
{
  PRINT_DIR_LEFT_TO_RIGHT = 0,
  PRINT_DIR_RIGHT_TO_LEFT,
  PRINT_DIR_TOP_TO_BOTTOM,
  PRINT_DIR_BOTTOM_TO_TOP
} PRINT_DIRECTION;

typedef enum
{
  SCROLL_DIR_LEFT_TO_RIGHT = 0,
  SCROLL_DIR_RIGHT_TO_LEFT,
  SCROLL_DIR_TOP_TO_BOTTOM,
  SCROLL_DIR_BOTTOM_TO_TOP
} SCROLL_DIRECTION;

typedef enum
{
  DISPLAY_EFFECT_SNAP = 0,
  DISPLAY_EFFECT_FADE,
  DISPLAY_EFFECT_WIPE
} DisplayEffect;

typedef enum
{
  EFFECT_DIR_LEFT_TO_RIGHT = 0,
  EFFECT_DIR_RIGHT_TO_LEFT,
  EFFECT_DIR_TOP_TO_BOTTOM,
  EFFECT_DIR_BOTTOM_TO_TOP
} EFFECT_DIRECTION;

typedef enum
{
  BORDER_TYPE_NONE = 0,
  BORDER_TYPE_RAISED,
  BORDER_TYPE_DEPRESSED,
  BORDER_TYPE_UNIFORM
} BORDER_TYPE;

typedef enum
{
  PEN_SIZE_SMALL = 0,
  PEN_SIZE_STANDARD,
  PEN_SIZE_LARGE,
  PEN_SIZE_INVALID
} PenSize;

typedef enum
{
  PEN_OFFSET_SUBSCRIPT = 0,
  PEN_OFFSET_NORMAL,
  PEN_OFFSET_SUPERSCRIPT,
  PEN_OFFSET_INVALID
} PenOffset;

typedef enum
{
  EDGE_TYPE_NONE = 0,
  EDGE_TYPE_RAISED,
  EDGE_TYPE_DEPRESSED,
  EDGE_TYPE_UNIFORM,
  EDGE_TYPE_LEFT_DROP_SHADOW,
  EDGE_TYPE_RIGHT_DROP_SHADOW,
  EDGE_TYPE_INVALID_1,
  EDGE_TYPE_INVALID_2
} EdgeType;

typedef enum
{
  FONT_STYLE_DEFAULT = 0,
  FONT_STYLE_MONO_SERIF,
  FONT_STYLE_PROP_SERIF,
  FONT_STYLE_MONO_SANS,
  FONT_STYLE_PROP_SANS,
  FONT_STYLE_CASUAL,
  FONT_STYLE_CURSIVE,
  FONT_STYLE_SMALLCAPS
} FontStyle;

typedef struct
{
  guint8 fg_color;
  guint8 fg_opacity;
  guint8 bg_color;
  guint8 bg_opacity;
  guint8 edge_color;
} cea708PenColor;

typedef struct
{
  gboolean span_start_flag;
  gboolean span_end_flag;
  gboolean span_txt_flag;

  gboolean span_next_flag;

  gboolean underline;
  gboolean italics;

  guint8 size;
  guint8 fg_color;
  guint8 bg_color;
  FontStyle font_style;
} cea708PangoSpanControl;

typedef struct
{
  PenSize pen_size;
  FontStyle font_style;
  TagType text_tag;
  PenOffset offset;
  gboolean italics;
  gboolean underline;
  EdgeType edge_type;
} cea708PenAttributes;

/* The char records one cell location in the window, with the character and all of its attributes */
typedef struct
{
  cea708PenColor pen_color;
  cea708PenAttributes pen_attributes;
  guint8 justify_mode;
  gunichar c;
} cea708char;


/* This struct keeps track of one cea-708 CC window. There are up to 8. As new
  * windows are created, the text they contain is visible on the screen (if the
  * window visible flag is set). When a window is deleted, all text within the
  * window is erased from the screen. Windows may be initialized and made visible
  * then hidden. Each transition should cause new text cues to be emitted as
  * text is displayed and removed from the screen.
  */
typedef struct
{
  /* The current attributes which will be used for the next text string */
  cea708PenColor pen_color;
  cea708PenAttributes pen_attributes;

  /* true to indicate the window has not been created.
   * set to true on delete, false on subsequent define command
   * if true, reset pen position to 0,0 on window creation
   */
  gboolean deleted;

  /* Text position */
  guint16 pen_row;
  guint16 pen_col;
  /* window display priority */
  guint8 priority;
  /* window position on screen 0-8 */
  guint8 anchor_point;
  /* 1 = anchor vertical/horizontal coordinates, 0 = physical screen coordinate, aka. rp */
  guint8 relative_position;
  /* vertical position of windows anchor point, 0-74 or if rp=1 then 0-99 */
  guint8 anchor_vertical;
  /* horz position of window anchor point, 0-209(16:9) 0-159(4:3) or if rp=1 then 0-99 */
  guint8 anchor_horizontal;
  /* vert position of upper left corner of window */
  gfloat screen_vertical;
  /* horz position of upper left corner of window */
  gfloat screen_horizontal;
  /* virtual rows of text - 1, (ex. rc=2 means there are 3 rows) */
  guint8 row_count;
  /* virtual columns of text, 0-41(16:9) 0-31(4:3) - 1 */
  guint8 column_count;
  /* 1 = fixes #rows of caption text, 0 = more rows may be added */
  guint8 row_lock;
  /* 1 = fixes #columns of caption text, 0 = more columns may be added */
  guint8 column_lock;
  /* TRUE = window is visible, FALSE = window not visible */
  gboolean visible;
  /* specifies 1 of 7 static preset window. attribute styles, during window create,
   * 0 = use style #1, during window update, 0 = no window, attributes will be changed
   */
  guint8 style_id;
  /* specifies 1 of 7 static preset pen attributes, during window create,
   * 0 = use pen style #1, during window update, 0 = do not change pen attributes
   */
  guint8 pen_style_id;
  /* timestamp when this window became visible */
  guint64 start_time;

  /* window attributes */
  guint8 justify_mode;
  guint8 print_direction;
  guint8 scroll_direction;
  gboolean word_wrap;
  guint8 display_effect;
  guint8 effect_direction;
  guint8 effect_speed;
  guint8 fill_color;
  guint8 fill_opacity;
  guint8 border_type;
  guint8 border_color;

  /* Character position offsets for the upper left corner of the window */
  guint v_offset;
  guint h_offset;

  /* The char array that text is written into, using the current pen position */
  cea708char text[WINDOW_MAX_ROWS][WINDOW_MAX_COLS];

  PangoLayout *layout;
  gdouble shadow_offset;
  gdouble outline_offset;
  guchar *text_image;
  gint image_width;
  gint image_height;
  gboolean updated;
} cea708Window;

struct _Cea708Dec
{
  /* output data storage */
  GSList *text_list;

  /* simulation of 708 CC windows */
  cea708Window *cc_windows[MAX_708_WINDOWS];
  guint8 current_window;
  gchar *default_font_desc;
  PangoContext *pango_context;

  /* a counter used to ignore bytes in CC text stream following commands */
  gint8 output_ignore;
  /* most recent timestamp from userdata */
  guint64 current_time;

  /* desired_service selects the service that will be decoded. If
   * desired_service = -1 (default) no decoding based on service number will
   * occur. Service #0 is reserved, and the valid range of service numbers
   * is 1-7. with 1 being primary caption service and 2 being the secondary
   * language service. If service_number is 7, then the extended_service_number is added and used instead of the service_number */
  gint8 desired_service;

  gboolean use_ARGB;
  gint width;
  gint height;
};

Cea708Dec *gst_cea708dec_create (PangoContext * pango_context);

void       gst_cea708dec_free (Cea708Dec *dec);

void
gst_cea708dec_set_service_number (Cea708Dec * decoder, gint8 desired_service);
gboolean
gst_cea708dec_process_dtvcc_packet (Cea708Dec * decoder, guint8 * dtvcc_buffer, gsize dtvcc_size);
void
gst_cea708dec_set_video_width_height (Cea708Dec * decoder, gint width, gint height);
void gst_cea708_decoder_init_debug(void);

  G_END_DECLS
#endif /* __GST_CEA708_DEC_H__ */