/***************************************************************************************
 *
 *  WRITEPAD(r): Handwriting Recognition Engine (HWRE) and components.
 *  Copyright (c) 2001-2016 PhatWare (r) Corp. All rights reserved.
 *
 *  Licensing and other inquires: <developer@phatware.com>
 *  Developer: Stan Miasnikov, et al. (c) PhatWare Corp. <http://www.phatware.com>
 *
 *  WRITEPAD HWRE 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
 *  AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 *  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 *  FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL PHATWARE CORP.
 *  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL,
 *  INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER,
 *  INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE, SAVINGS
 *  OR REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR NOT PHATWARE CORP.
 *  HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
 *  POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
 *  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with WritePad.  If not, see <http://www.gnu.org/licenses/>.
 *
 **************************************************************************************/

#include "hwr_sys.h"
#include "ams_mg.h"
#include "precutil.h"
#include "xr_names.h"
#include  <string.h>


#define CUT_MWORD         ON    // Enables mword on big spaces in words
#define CUT_LOCATIONS    OFF    // Enables calculation of locations for dict len cut

/* *************************************************************** */
/* *            Set XrData marks at places destined for MW bndrs * */
/* *************************************************************** */

_INT  SetMultiWordMarksDash(p_xrdata_type xrdata)
{
    _INT       i;
    _INT       mword = 0;
    _INT       xrinp_len = xrdata->len;
    p_xrd_type xrd = xrdata->xrd;
    
    /* -------------- Betw-word dash multiword ------------------------------ */
    
    for (i = 1; i < xrinp_len-4; i ++)
    {
        if (IS_CLEAR_LINK((*xrd)[i].xr.type))
        {
            if (((*xrd)[i+1].xr.type == X_XT_ST || (*xrd)[i+1].xr.type == X_ST) &&
                IS_CLEAR_LINK((*xrd)[i+2].xr.type))
            {
                WSF_SET((*xrd)[i].xr.attrib, WS_SEGM_NOSEG);
                WSF_SET((*xrd)[i+2].xr.attrib, WS_SEGM_NOSP);
                mword = 1;
            }
        }
    }
    
    return mword;
}

/* *************************************************************** */
/* *            Set XrData marks at places destined for MW bndrs * */
/* *************************************************************** */

_INT  SetMultiWordMarksWS(_INT level, p_xrdata_type xrdata, p_rc_type rc)
{
    _INT            i, j, k, n, v, p, ns;
    _INT            xs;
    _INT            mword = 0;
    _INT            xrinp_len = xrdata->len;
    p_PS_point_type ptr;
    p_xrd_type      xrd = xrdata->xrd;
    p_PS_point_type trace = rc->trace;
    p_ws_word_info_type wswi = (p_ws_word_info_type)rc->p_ws_wi;
    
#define SMW_LEVEL1   (70)
#define SMW_LEVEL2   (30)
#define SMW_LEVEL3  (-30)
#define SMW_LEVEL4  (-70)
#define SMW_LEVEL5  (-85)
    
    /* -------------- Big spaces Mword and penalties ------------------------ */
    
    if (wswi == _NULL)
        goto err;
    
    for (j = 0; j < WSW_MAX_VALUES; j ++) // Search for UNSURE stroke
    {
        if ((ns = wswi->s_nums[j]) == 0)
            continue;
        if (HWRAbs(wswi->s_surs[j]) > level)
            continue; // Set marks only on unsure gaps
        
        for (i = 1, k = 1, ptr = trace+1; k < rc->ii; ptr ++, k ++)
        {
            if (ptr->y < 0) // We are at break location
            {
                if (i == ns) // We are at the end of desired stroke
                {
                    v = k-1;  // Number of last point of the desired stroke
                    
                    for (i = 1, p = 0; i < xrinp_len-1; i ++)
                    {
                        if (!IS_XR_LINK((*xrd)[i].xr.type))
                            continue;
                        
                        for (n = v, xs = 0; n > 0 && trace[n].y >= 0; n --)
                        {
                            if (xs < trace[n].x)
                                xs = trace[n].x;
                        }
                        if ((xs >= (*xrd)[i].box_left && xs <= (*xrd)[i].box_right) || v == (*xrd)[i].begpoint)
                        {
                            mword = 1;
                            p     = i;
                        }
                    }
                    
                    n = 0;
                    i = wswi->s_surs[j];
                    if (i < SMW_LEVEL1)
                        n = 1;
                    if (i < SMW_LEVEL2)
                        n = 2;
                    if (i < SMW_LEVEL3)
                        n = 3;
                    if (i < SMW_LEVEL4)
                        n = 4;
                    
                    if (p)
                        WSF_SET((*xrd)[p].xr.attrib, WS_SEGM_HISEG+n);
                    
                    break;
                }
                i++;
            }
        } // Trace search cycle
    } // word strokes cycle end
    
    return mword;
err:
    return 0;
}

/* *************************************************************** */
/* *           Set strtokes' unsure marks array                  * */
/* *************************************************************** */

_INT  SetStrokeSureValuesWS(_INT fl_carry, _INT num_word, p_ws_results_type wsr, p_ws_word_info_type wswi)
{
    _INT i, j, k, n, t;
    _INT w, p, loc;
    p_word_strokes_type wstr = &((*wsr->pwsa)[num_word]);
    
    for (n = 0; n < WSW_MAX_VALUES && wswi->s_nums[n] != 0; n ++)
        ;
    
    if (n >= WSW_MAX_VALUES)
        goto err;
    
    for (i = 0; i < wstr->num_strokes; i ++) // Process all strokes
    {
        w = 100; loc = 0;
        for (j = 0; j < wstr->num_strokes-1; j ++) // Search max unsure stroke (from what is left)
        {
            for (k = p = 0; k < n; k ++) if (wswi->s_nums[k]-1 == j)
            {
                p = 1;
                break;
            }
            if (p)
                continue;
            
            if (w > (t = HWRAbs(wsr->k_surs[wstr->first_stroke_index + j])))
            {
                w = t;
                loc = j+1;
            }
        }
        
        if (w == 100) break; // All done, pora ...
        
        wswi->s_nums[n] = (_UCHAR)(loc - ((fl_carry) ? 1:0));
        wswi->s_surs[n] = (_SCHAR)(wsr->k_surs[wstr->first_stroke_index+loc-1]);
        if (++n >= WSW_MAX_VALUES)
            break;
    }
    
    return 0;
err:
    return 1;
}

/* *************************************************************** */
/* ****** BaseLine definition support routines ******************* */
/* *************************************************************** */

/* *************************************************************** */
/* *             Set reference baseline for Stroka               * */
/* *************************************************************** */

_INT SetRCB(p_RCB_inpdata_type p_inp, p_stroka_data p_stroka)
{
    _INT  pos, size, dn_pos, size_sure, pos_sure, shift;
    
    size = p_stroka->size_in = 0;
    dn_pos = p_stroka->dn_pos_in = 0;
    size_sure = p_stroka->size_sure_in = 0;
    pos_sure = p_stroka->pos_sure_in  = 0;
    
    GetInkBox(p_inp->trace, p_inp->num_points, &(p_stroka->box));
    
    if (p_inp->flags & RCBF_WSBORD) // Word segm stroka present
    {
        size = p_inp->ws_size;
        dn_pos = p_inp->ws_dn_pos;
        size_sure = 50;
        if (p_inp->flags & RCBF_NEWLINE)
            pos_sure = 0;
        else
            pos_sure  = 50;
    }
    // Prev word stroka present
    if ((p_inp->flags & RCBF_PREVBORD) && !(p_inp->flags & RCBF_NEWAREA))
    {
        size= p_inp->prv_size;
        size_sure = p_inp->prv_size_sure;
        
        if ((p_inp->flags & RCBF_WSBORD) == 0) // No segmentation
        {
            dn_pos    = p_inp->prv_dn_pos;
            pos_sure  = p_inp->prv_pos_sure;
            pos = GetAvePos(p_inp->trace, p_inp->num_points);
            if (HWRAbs(pos - (dn_pos-size/2)) > size) pos_sure = 0;
        }
        else
        {
            if ((p_inp->flags & RCBF_NEWLINE) == 0) // Same line
            {
                dn_pos = p_inp->prv_dn_pos;
                pos_sure  = p_inp->prv_pos_sure;
            }
        }
    }
    
    
    if (p_inp->flags & RCBF_BOXBORD) // Word segm stroka present
    {
        size      = p_inp->bx_size;
        dn_pos    = p_inp->bx_dn_pos;
        size_sure = 100;
        pos_sure  = 100;
        // Correct Box est
        if (p_stroka->box.bottom > 0)
        {
            if (p_stroka->box.bottom - p_stroka->box.top > size/2)
            {
                if (dn_pos > p_stroka->box.bottom)
                {
                    shift  = dn_pos - p_stroka->box.bottom;
                    if (shift > size/2)
                        shift = size/2;
                    dn_pos -= shift;
                    if (dn_pos - size < p_stroka->box.top)
                        size -= p_stroka->box.top - (dn_pos - size);
                }
            }
        }
    }
    
    p_stroka->size_in      = (_SHORT)size;
    p_stroka->dn_pos_in    = (_SHORT)dn_pos;
    p_stroka->size_sure_in = (_SHORT)size_sure;
    p_stroka->pos_sure_in  = (_SHORT)pos_sure;
    
    return 0;
}

/* *************************************************************** */
/* *            Extract baseline info from WS structures         * */
/* *************************************************************** */
_INT GetWSBorder(_INT nword, p_ws_results_type wsr, p_INT psize, p_INT ppos, p_INT nl)
{
    p_word_strokes_type wstr;
    p_word_strokes_type pwstr;
    
    if (wsr == _NULL)
        goto err;
    if (nword >= wsr->num_words)
        goto err;
    
    wstr   = &((*wsr->pwsa)[nword]);
    *psize = wstr->ave_h_bord;
    *ppos  = wstr->word_mid_line + wstr->ave_h_bord/2;
    
    if (!(wstr->flags & WS_FL_SPSURE))
        goto err;
    if (nword < 1)
        goto err;
    pwstr = &((*wsr->pwsa)[nword-1]);
    
    if (nword > 1)
    {
        if ((pwstr-1)->flags & WS_FL_CARRYDASH)
            pwstr --;
    }
    *nl = (wstr->line_num != pwstr->line_num);
    
    return 0;
err:
    *nl = 1;
    return 1;
}

/* *************************************************************** */
/* *          Get box of given trace                             * */
/* *************************************************************** */
_INT GetInkBox(_TRACE pt, _INT np, p_RECT prect)
{
    _INT   i;
    _INT   xmin, xmax, ymin, ymax;
    _TRACE tr = pt;
    
    if (tr == _NULL || np < 3)
        goto err;
    
    xmax = ymax = 0;
    xmin = ymin = 32000;
    
    for (i = 0; i < np; i ++, tr ++)
    {
        if (tr->y < 0)
            continue;
        
        if (tr->x < xmin) xmin = tr->x;
        if (tr->x > xmax) xmax = tr->x;
        if (tr->y < ymin) ymin = tr->y;
        if (tr->y > ymax) ymax = tr->y;
    }
    
    prect->left   = (_SHORT)xmin;
    prect->top    = (_SHORT)ymin;
    prect->right  = (_SHORT)xmax;
    prect->bottom = (_SHORT)ymax;
    
    return 0;
err:
    prect->left = prect->top = prect->right = prect->bottom = (_SHORT)0;
    return 1;
}

/* *************************************************************** */
/* *             Set reference baseline for Stroka               * */
/* *************************************************************** */

_INT GetAvePos(_TRACE trace, _INT num_points)
{
    _INT  i, j;
    _LONG y_sum;
    
    if (trace == _NULL || num_points < 3)
        goto err;
    
    for (i = 0, y_sum = 0, j = 0; i < num_points; i ++)
    {
        if (trace[i].y < 0)
            continue;
        
        y_sum += trace[i].y; j ++;
    }
    
    if (j == 0)
        goto err;
    
    return (_INT)(y_sum/j);
err:
    return 0;
}

/* ************************************************************************ */
/* * Allocate memory for new xrdata of requested size and clean it        * */
/* ************************************************************************ */

int AllocXrdata(p_xrdata_type xrdata, int size)
{
    
    if (xrdata == _NULL)
        goto err;
    if (size == 0 || size > XRINP_SIZE)
        goto err;
    
    xrdata->xrd = (p_xrd_type)HWRMemoryAlloc((_UINT)(sizeof(xrd_el_type) * size));
    
    if (xrdata->xrd == _NULL)
        goto err;
    xrdata->size = size;
    xrdata->len  = 0;
    
    HWRMemSet(xrdata->xrd, 0, (_UINT)(sizeof(xrd_el_type) * size));
    
    return 0;
err:
    return 1;
}

/* ************************************************************************ */
/* *  Deallocate memory used by xrdata                                    * */
/* ************************************************************************ */

int FreeXrdata(p_xrdata_type xrdata)
{
    
    if (xrdata == _NULL)
        goto err;
    if (xrdata->xrd == _NULL)
        goto err;
    
    HWRMemoryFree(xrdata->xrd);
    xrdata->xrd  = _NULL;
    xrdata->len  = 0;
    xrdata->size = 0;
    
    return 0;
err:
    return 1;
}

