/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Miscellaneous helper API declarations
 *
 * Copyright (c) 2007 Michael Sevakis
 *
 * 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.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#ifndef MPEG_MISC_H
#define MPEG_MISC_H

/* Miscellaneous helpers */
#ifndef ALIGNED_ATTR
#define ALIGNED_ATTR(x) __attribute__((aligned(x)))
#endif

#include "disk_buf.h"

/* Generic states for when things are too simple to care about naming them */
enum state_enum
{
    STATE0 = 0,
    STATE1,
    STATE2,
    STATE3,
    STATE4,
    STATE5,
    STATE6,
    STATE7,
    STATE8,
    STATE9,
};

/* Macros for comparing memory bytes to a series of constant bytes in an
   efficient manner - evaluate to true if corresponding bytes match */
#if defined (CPU_ARM)
/* ARM must load 32-bit values at addres % 4 == 0 offsets but this data
   isn't aligned nescessarily, so just byte compare */
#define CMP_3_CONST(_a, _b) \
    ({  int _x;                             \
        asm volatile (                      \
            BEGIN_ARM_ASM_SYNTAX_UNIFIED    \
            "ldrb   %[x], [%[a], #0]  \n"   \
            "eors   %[x], %[x], %[b0] \n"   \
            "ldrbeq %[x], [%[a], #1]  \n"   \
            "eorseq %[x], %[x], %[b1] \n"   \
            "ldrbeq %[x], [%[a], #2]  \n"   \
            "eorseq %[x], %[x], %[b2] \n"   \
            END_ARM_ASM_SYNTAX_UNIFIED      \
            : [x]"=&r"(_x)                  \
            : [a]"r"(_a),                   \
              [b0]"i"(((_b) >> 24) & 0xff), \
              [b1]"i"(((_b) >> 16) & 0xff), \
              [b2]"i"(((_b) >>  8) & 0xff)  \
        );                                  \
        _x == 0; })

#define CMP_4_CONST(_a, _b) \
    ({  int _x;                             \
        asm volatile (                      \
            BEGIN_ARM_ASM_SYNTAX_UNIFIED    \
            "ldrb   %[x], [%[a], #0]  \n"   \
            "eors   %[x], %[x], %[b0] \n"   \
            "ldrbeq %[x], [%[a], #1]  \n"   \
            "eorseq %[x], %[x], %[b1] \n"   \
            "ldrbeq %[x], [%[a], #2]  \n"   \
            "eorseq %[x], %[x], %[b2] \n"   \
            "ldrbeq %[x], [%[a], #3]  \n"   \
            "eorseq %[x], %[x], %[b3] \n"   \
            END_ARM_ASM_SYNTAX_UNIFIED      \
            : [x]"=&r"(_x)                  \
            : [a]"r"(_a),                   \
              [b0]"i"(((_b) >> 24) & 0xff), \
              [b1]"i"(((_b) >> 16) & 0xff), \
              [b2]"i"(((_b) >>  8) & 0xff), \
              [b3]"i"(((_b)      ) & 0xff)  \
        );                                  \
        _x == 0; })

#elif defined (CPU_COLDFIRE)
/* Coldfire can just load a 32 bit value at any offset but ASM is not the
   best way to integrate this with the C code */
#define CMP_3_CONST(a, b) \
    (((*(uint32_t *)(a) >> 8) == ((uint32_t)(b) >> 8)))

#define CMP_4_CONST(a, b) \
    ((*(uint32_t *)(a) == (b)))

#else
/* Don't know what this is - use bytewise comparisons */
#define CMP_3_CONST(a, b) \
    (( ((a)[0] ^ (((b) >> 24) & 0xff)) | \
       ((a)[1] ^ (((b) >> 16) & 0xff)) | \
       ((a)[2] ^ (((b) >>  8) & 0xff)) ) == 0)

#define CMP_4_CONST(a, b) \
    (( ((a)[0] ^ (((b) >> 24) & 0xff)) | \
       ((a)[1] ^ (((b) >> 16) & 0xff)) | \
       ((a)[2] ^ (((b) >>  8) & 0xff)) | \
       ((a)[3] ^ (((b)      ) & 0xff)) ) == 0)
#endif /* CPU_* */


/** Streams **/

/* Convert PTS/DTS ticks to our clock ticks */
#define TS_TO_TICKS(pts) ((uint64_t)CLOCK_RATE*(pts) / TS_SECOND)
/* Convert our clock ticks to PTS/DTS ticks */
#define TICKS_TO_TS(ts)  ((uint64_t)TS_SECOND*(ts) / CLOCK_RATE)
/* Convert timecode ticks to our clock ticks */
#define TC_TO_TICKS(stamp) ((uint64_t)CLOCK_RATE*(stamp) / TC_SECOND)
/* Convert our clock ticks to timecode ticks */
#define TICKS_TO_TC(stamp) ((uint64_t)TC_SECOND*(stamp) / CLOCK_RATE)
/* Convert timecode ticks to timestamp ticks */
#define TC_TO_TS(stamp) ((stamp) / 600)

/*
 * S = start position, E = end position
 *
 * pos:
 *     initialize to search start position (S)
 *
 * len:
 *     initialize to = ABS(S-E)
 *     scanning = remaining bytes in scan direction
 *
 * dir:
 *     scan direction; >= 0 == forward, < 0 == reverse
 *
 * margin:
 *     amount of data to right of cursor - initialize by stream_scan_normalize
 *
 * data:
 *     Extra data used/returned by the function implemented
 *
 * Forward scan:
 *     S      pos         E
 *     |       *<-margin->| dir->
 *     |       |<--len--->|
 *
 * Reverse scan:
 *     E      pos         S
 *     |<-len->*<-margin->| <-dir
 *     |       |          |
 */
struct stream_scan
{
    off_t   pos;    /* Initial scan position (file offset) */
    ssize_t len;    /* Maximum length of scan */
    off_t   dir;    /* Direction - >= 0; forward, < 0 backward */
    ssize_t margin; /* Used by function to track margin between position and data end */
    intptr_t data;  /* */
    struct dbuf_l2_cache l2;
};

#define SSCAN_REVERSE (-1)
#define SSCAN_FORWARD 1

/* Initializes the cursor */
void stream_scan_init(struct stream_scan *sk);

/* Ensures direction is -1 or 1 and margin is properly initialized */
void stream_scan_normalize(struct stream_scan *sk);

/* Moves a scan cursor. If amount is positive, the increment is in the scan
 * direction, otherwise opposite the scan direction */
void stream_scan_offset(struct stream_scan *sk, off_t by);

/** Time helpers **/
struct hms
{
    unsigned int hrs;
    unsigned int min;
    unsigned int sec;
    unsigned int frac;
};

void ts_to_hms(uint32_t ts, struct hms *hms);
void hms_format(char *buf, size_t bufsize, struct hms *hms);

/** Maths **/

/* Moving average */
#define AVERAGE(var, x, count) \
    ({ typeof (count) _c = (count);   \
       ((var) * (_c-1) + (x)) / (_c); })

/* Multiply two unsigned 32-bit integers yielding a 64-bit result and
 * divide by another unsigned 32-bit integer to yield a 32-bit result.
 * Rounds to nearest with saturation. */
uint32_t muldiv_uint32(uint32_t multiplicand,
                       uint32_t multiplier,
                       uint32_t divisor);


/** Lists **/

/* Does the list have any members? */
bool list_is_empty(void **list);

/* Is the item inserted into a particular list? */
bool list_is_member(void **list, void *item);

/* Removes an item from a list - returns true if item was found
 * and thus removed. */
bool list_remove_item(void **list, void *item);

/* Adds a list item, insert last, if not already present. */
void list_add_item(void **list, void *item);

/* Clears the entire list. */
void list_clear_all(void **list);

/* Enumerate all items in the array. */
typedef bool (*list_enum_callback_t)(void *item, void* data);

void list_enum_items(void **list,
                     list_enum_callback_t callback,
                     void *data);


/** System events **/

/* Clear event */
void mpeg_sysevent_clear(void);

/* Set to ACTION_STD_CANCEL */
void mpeg_sysevent_set(void);

/* Get event code */
long mpeg_sysevent(void);

/* Call with a system event code and used as menu callback */
int mpeg_sysevent_callback(int btn, const struct menu_item_ex *menu,
                           struct gui_synclist *this_list);

/* Handle recorded event */
void mpeg_sysevent_handle(void);


/** Buttons **/

/* Get button codes while remembering important events for later
 * processing; return of ACTION_STD_CANCEL means plugin should
 * abort and handle the event */
int mpeg_button_get(int timeout);

#endif /* MPEG_MISC_H */
