#define MAX_PNTS 100

void DrawOldRange(struct IClass *,Object *);
void DrawNewRange(struct IClass *,Object *);
void DrawWave(struct IClass *,Object *);
void DrawEnv(struct IClass *,Object *);
void DrawEnvPnt(struct IClass *,Object *);

LONG FindPnt(struct IClass *, Object *, ULONG, UBYTE);
ULONG FindSamp(struct IClass *, Object *, ULONG, UBYTE);
ULONG Bytes_To_Samples(struct IClass *, Object *, ULONG);
ULONG Samples_To_Bytes(struct IClass *, Object *, ULONG);


struct WaveData
{
    BYTE *waveform;
    ULONG length;
    LONG curs, start, end, oldstart, oldend, shown, visible, loop_start, loop_end;
    ULONG draw, quickdraw, vlinegap, type;
    ULONG used, current;
    UWORD x[MAX_PNTS], lev[MAX_PNTS];
    UWORD oldx, oldlev;
    UWORD editmode;
};

#define MUIM_Waveform_Update        0x80500000
#define MUIM_Envelope_AddPnt        0x80500001
#define MUIM_Envelope_DelPnt        0x80500002

#define MUIA_Waveform_Waveform      0x8022
#define MUIA_Waveform_Length        0x8023
#define MUIA_Waveform_Curs          0x8024
#define MUIA_Waveform_RangeStart    0x8025
#define MUIA_Waveform_RangeEnd      0x8026
#define MUIA_Waveform_ShownStart    0x8027
#define MUIA_Waveform_Visible       0x8028
#define MUIA_Waveform_Draw          0x8029
#define MUIA_Envelope_Used          0x8030
#define MUIA_Envelope_Current       0x8031
#define MUIA_Envelope_X             0x8032
#define MUIA_Envelope_Level         0x8033
#define MUIA_Waveform_EditMode      0x8034
#define MUIA_Waveform_VLineGap      0x8035
#define MUIA_Waveform_Type          0x8036
#define MUIA_Waveform_RangeStartSam 0x8037
#define MUIA_Waveform_RangeEndSam   0x8038

/*waveform draw modes*/
#define WDM_NONE        0
#define WDM_LINE        1
#define WDM_PIXEL1      2
#define WDM_PIXEL2      3
#define WDM_PIXEL3      4
#define WDM_PIXELALL    5
#define WDM_VFILLHALF   6
#define WDM_VFILL       7
#define WDM_RANGE       8
#define WDM_HGRID       16
#define WDM_VGRID       32
#define WDM_ENVBLOCKS   64
#define WDM_ENVLINES    128

/*quick draw modes*/
#define QDM_ALL         0
#define QDM_RANGE       1
#define QDM_ENVPNT      2
#define QDM_WAVE        3

/*edit modes*/
#define EM_NONE         0
#define EM_RANGE        1
#define EM_RANGELOOP    2
#define EM_ENV          3

#include "waveeditclassdraw.c"

SAVEDS ULONG mNew(struct IClass *cl,Object *obj,Msg msg)
{
    struct WaveData *data;

    if (!(obj = (Object *)DoSuperMethodA(cl,obj,msg)))
            return(0);

    data = INST_DATA(cl,obj);

    data->waveform = NULL;
    data->length = NULL;
    data->curs = data->start = data->end = data->oldstart = data->oldend = data->draw = NULL;
    data->current =  data->quickdraw = 0;
    data->draw = WDM_LINE | WDM_RANGE | WDM_HGRID | WDM_VGRID;
    data->vlinegap = 10000;
    data->type = STYPE_M8;
    data->editmode = EM_RANGE;
    data->used = 2;
    for(ULONG n = 0; n < MAX_PNTS; n++)
    {
        data->lev[n]  = 32768;
        data->x[n] = 32768;
    }
    data->x[0] = 0;
    data->x[1] = 65535;
    data->oldx = data->x[data->current];
    data->oldlev = data->lev[data->current];

    msg->MethodID = OM_SET;
    DoMethodA(obj, (Msg)msg);
    msg->MethodID = OM_NEW;

    return((ULONG)obj);
}



SAVEDS ULONG mDispose(struct IClass *cl,Object *obj,Msg msg)
{
        /* OM_NEW didnt allocate anything */
        return(DoSuperMethodA(cl,obj,msg));
}


SAVEDS ULONG mSet(struct IClass *cl,Object *obj,Msg msg)
{
    struct WaveData *data = INST_DATA(cl,obj);
    struct TagItem *tags,*tag;

    for (tags=((struct opSet *)msg)->ops_AttrList;tag=NextTagItem(&tags);)
    {
        switch (tag->ti_Tag)
        {
            case MUIA_Waveform_Waveform:
                data->waveform = (BYTE *)tag->ti_Data;
                /*MUI_Redraw(obj,MADF_DRAWOBJECT);*/
                break;

            case MUIA_Waveform_Length:
                data->length = Bytes_To_Samples(cl, obj, tag->ti_Data);
                /*MUI_Redraw(obj,MADF_DRAWOBJECT);*/
                break;

            case MUIA_Waveform_RangeStart:
                data->start = Bytes_To_Samples(cl, obj, tag->ti_Data);
                data->quickdraw = QDM_RANGE;
                MUI_Redraw(obj,MADF_DRAWUPDATE);
                data->quickdraw = QDM_ALL;
                break;

            case MUIA_Waveform_RangeEnd:
                data->end = Bytes_To_Samples(cl, obj, tag->ti_Data);
                data->quickdraw = QDM_RANGE;
                MUI_Redraw(obj,MADF_DRAWUPDATE);
                data->quickdraw = QDM_ALL;
                break;

            case MUIA_Waveform_RangeStartSam:
                data->start = tag->ti_Data;
                data->quickdraw = QDM_RANGE;
                MUI_Redraw(obj,MADF_DRAWUPDATE);
                data->quickdraw = QDM_ALL;
                break;

            case MUIA_Waveform_RangeEndSam:
                data->end = tag->ti_Data;
                data->quickdraw = QDM_RANGE;
                MUI_Redraw(obj,MADF_DRAWUPDATE);
                data->quickdraw = QDM_ALL;
                break;

            case MUIA_Waveform_Curs:
                data->curs = Bytes_To_Samples(cl, obj, tag->ti_Data);
                /*MUI_Redraw(obj,MADF_DRAWUPDATE);*/
                break;

            case MUIA_Waveform_ShownStart:
                data->shown = (LONG)Bytes_To_Samples(cl, obj, tag->ti_Data);
                /*MUI_Redraw(obj,MADF_DRAWUPDATE);*/
                break;

            case MUIA_Waveform_Visible:
                data->visible = Bytes_To_Samples(cl, obj, tag->ti_Data);
                /*MUI_Redraw(obj,MADF_DRAWOBJECT);*/
                break;

            case MUIA_Waveform_Draw:
                data->draw = (ULONG)tag->ti_Data;
                /*MUI_Redraw(obj,MADF_DRAWOBJECT);*/
                break;

            case MUIA_Envelope_Used:
                data->used = (ULONG)tag->ti_Data;
                data->x[data->used -1] = 65535;
                MUI_Redraw(obj,MADF_DRAWOBJECT);
                break;

            case MUIA_Envelope_Current:
                data->current = (ULONG)tag->ti_Data;
                /*MUI_Redraw(obj,MADF_DRAWUPDATE);*/
                break;

            case MUIA_Envelope_X:
                data->x[data->current] = (UWORD)tag->ti_Data;
                data->quickdraw = QDM_ENVPNT;
                MUI_Redraw(obj,MADF_DRAWUPDATE);
                data->quickdraw = QDM_ALL;
                break;

            case MUIA_Envelope_Level:
                data->lev[data->current] = (UWORD)tag->ti_Data;
                data->quickdraw = QDM_ENVPNT;
                MUI_Redraw(obj,MADF_DRAWUPDATE);
                data->quickdraw = QDM_ALL;
                break;

            case MUIA_Waveform_EditMode:
                data->editmode = (UWORD)tag->ti_Data;
                break;

            case MUIA_Waveform_VLineGap:
                data->vlinegap = (ULONG)tag->ti_Data;
                MUI_Redraw(obj,MADF_DRAWOBJECT);
                break;

            case MUIA_Waveform_Type:
                data->type = (ULONG)tag->ti_Data;
                break;

        }

        /*if (data->start <0) data->start =0;
        if (data->start > data->length) data->start = data->length;
        if (data->end <0) data->end =0;
        if (data->end > data->length) data->end = data->length;*/
        if (data->shown > data->length) data->shown = 0; /*set(obj,MUIA_Waveform_ShownStart,0);*/
        if ((data->visible + data->shown) > data->length) data->visible = data->length - data->shown; /*set(obj,MUIA_Waveform_Visible,data->length - data->shown);*/
    }

    return(DoSuperMethodA(cl,obj,msg));
}


static ULONG mGet(struct IClass *cl,Object *obj,Msg msg)
{
    struct WaveData *data = INST_DATA(cl,obj);
    ULONG *store = ((struct opGet *)msg)->opg_Storage;

    switch (((struct opGet *)msg)->opg_AttrID)
    {
        case MUIA_Waveform_Waveform: *store = (ULONG)data->waveform; return(TRUE);

        case MUIA_Waveform_Length: *store = Samples_To_Bytes(cl, obj, data->length); return(TRUE);

        case MUIA_Waveform_RangeStart:
            if(data->start > data->end) /*make sure the start is before the end*/
            {
                LONG temp = data->end;
                data->end = data->start;
                data->start = temp;
            }
            *store = Samples_To_Bytes(cl, obj, data->start); return(TRUE);

        case MUIA_Waveform_RangeEnd:
            if(data->start > data->end) /*make sure the start is before the end*/
            {
                LONG temp = data->end;
                data->end = data->start;
                data->start = temp;
            }
            *store = Samples_To_Bytes(cl, obj, data->end); return(TRUE);

        case MUIA_Waveform_RangeStartSam:
            if(data->start > data->end) /*make sure the start is before the end*/
            {
                LONG temp = data->end;
                data->end = data->start;
                data->start = temp;
            }
            *store = data->start; return(TRUE);

        case MUIA_Waveform_RangeEndSam:
            if(data->start > data->end) /*make sure the start is before the end*/
            {
                LONG temp = data->end;
                data->end = data->start;
                data->start = temp;
            }
            *store = data->end; return(TRUE);

        case MUIA_Waveform_Curs: *store = Samples_To_Bytes(cl, obj, data->curs); return(TRUE);

        case MUIA_Waveform_ShownStart: *store = Samples_To_Bytes(cl, obj, data->shown); return(TRUE);

        case MUIA_Waveform_Visible: *store = Samples_To_Bytes(cl, obj, data->visible); return(TRUE);

        case MUIA_Waveform_Draw: *store = (ULONG)data->draw; return(TRUE);

        case MUIA_Envelope_Used: *store = (ULONG)data->used; return(TRUE);

        case MUIA_Envelope_Current: *store = (ULONG)data->current; return(TRUE);

        case MUIA_Envelope_X: *store = (ULONG)data->x[data->current]; return(TRUE);

        case MUIA_Envelope_Level: *store = (ULONG)data->lev[data->current]; return(TRUE);

        case MUIA_Waveform_EditMode: *store = (ULONG)data->lev[data->editmode]; return(TRUE);

        case MUIA_Waveform_VLineGap: *store = (ULONG)data->vlinegap; return(TRUE);

        case MUIA_Waveform_Type: *store = (ULONG)data->type; return(TRUE);
    }

    return(DoSuperMethodA(cl,obj,msg));
}


ULONG Bytes_To_Samples(struct IClass *cl, Object *obj, ULONG bytes)
{
    struct WaveData *data = INST_DATA(cl,obj);
    ULONG samples;
    if (data->type == STYPE_M8) samples = bytes;
    if ((data->type == STYPE_M16) || (data->type == STYPE_S8)) samples = bytes /2;
    if ((data->type == STYPE_M32) || (data->type == STYPE_S16)) samples = bytes /4;
    if (data->type == STYPE_S32) samples = bytes /8;
    return(samples);
}


ULONG Samples_To_Bytes(struct IClass *cl, Object *obj, ULONG samples)
{
    struct WaveData *data = INST_DATA(cl,obj);
    ULONG bytes;
    if (data->type == STYPE_M8) bytes = samples;
    if ((data->type == STYPE_M16) || (data->type == STYPE_S8)) bytes = samples *2;
    if ((data->type == STYPE_M32) || (data->type == STYPE_S16)) bytes = samples *4;
    if (data->type == STYPE_S32) bytes = samples *8;
    return(bytes);
}

SAVEDS ULONG mSetup(struct IClass *cl,Object *obj,Msg msg)
{
    struct MyData *data = INST_DATA(cl,obj);

    if (!DoSuperMethodA(cl,obj,msg))
            return(FALSE);

    MUI_RequestIDCMP(obj, IDCMP_MOUSEBUTTONS);

    return(TRUE);
}


SAVEDS ULONG mCleanup(struct IClass *cl,Object *obj,Msg msg)
{
    struct MyData *data = INST_DATA(cl,obj);

    MUI_RejectIDCMP(obj, IDCMP_MOUSEBUTTONS);

    return(DoSuperMethodA(cl,obj,msg));
}


SAVEDS ULONG mAskMinMax(struct IClass *cl,Object *obj,struct MUIP_AskMinMax *msg)
{
    DoSuperMethodA(cl,obj,(VOID *)msg);

    msg->MinMaxInfo->MinWidth  += 50;
    msg->MinMaxInfo->DefWidth  += 300;
    msg->MinMaxInfo->MaxWidth  += 1000;

    msg->MinMaxInfo->MinHeight += 50;
    msg->MinMaxInfo->DefHeight += 70;
    msg->MinMaxInfo->MaxHeight += 500;

    return(0);
}


SAVEDS ULONG mHandleInput(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg)
{
    #define _between(a,x,b) ((x)>=(a) && (x)<=(b))
    #define _isinobject(x,y) (_between(_mleft(obj),(x),_mright(obj)) && _between(_mtop(obj),(y),_mbottom(obj)))


    struct WaveData *data = INST_DATA(cl,obj);

    if (!msg->imsg) return (0);

    if ((data->editmode == EM_RANGE) || (data->editmode == EM_RANGELOOP))
    {
        switch (msg->imsg->Class)
        {
            case IDCMP_MOUSEBUTTONS:
            {
                if (msg->imsg->Code==SELECTDOWN)
                {
                    if (_isinobject(msg->imsg->MouseX,msg->imsg->MouseY))
                    {
                        /*Check to edit loop...*/
                        /*lleft = ;
                        if((_between(lleft,msg->imsg->MouseX,lright)) && (_between(ltop,msg->imsg->MouseY,lbottom)))
                        {
                        }
                        else*/

                        data->start = (msg->imsg->MouseX - _mleft(obj)) * data->visible / _mwidth(obj) + data->shown;
                        data->end = (msg->imsg->MouseX - _mleft(obj)) * data->visible / _mwidth(obj) + data->shown;
                        data->quickdraw = QDM_RANGE;
                        MUI_Redraw(obj,MADF_DRAWUPDATE);
                        MUI_RequestIDCMP(obj,IDCMP_MOUSEMOVE);
                        data->quickdraw = QDM_ALL;
                    }
                }
                else
                {
                    MUI_RejectIDCMP(obj,IDCMP_MOUSEMOVE);
                    set(obj, MUIA_Waveform_RangeStartSam, data->start);
                    set(obj, MUIA_Waveform_RangeEndSam, data->end);
                }
            }
            break;

            case IDCMP_MOUSEMOVE:
            {
                if (_between(_mleft(obj),msg->imsg->MouseX,_mright(obj)) )
                {
                    data->quickdraw = QDM_RANGE;
                    data->end = (msg->imsg->MouseX - _mleft(obj)) * data->visible / _mwidth(obj) + data->shown;
                    MUI_Redraw(obj,MADF_DRAWUPDATE);
                    data->quickdraw = QDM_ALL;
                }
                else
                {
                    if (msg->imsg->MouseX > _mright(obj))
                    {
                        if (!((data->shown += (msg->imsg->MouseX - _mright(obj)) * data->visible / 100) < (data->length - data->visible))) data->shown = (data->length - data->visible);
                        data->end = (msg->imsg->MouseX - _mleft(obj)) * data->visible / _mwidth(obj) + data->shown;
                        /*MUI_Redraw(obj,MADF_DRAWUPDATE);*/
                        SetAttrs(obj,MUIA_Waveform_ShownStart, Samples_To_Bytes(cl ,obj ,data->shown), TAG_DONE);
                    }
                    if (msg->imsg->MouseX < _mleft(obj))
                    {
                        if (!((data->shown += (msg->imsg->MouseX - _mleft(obj)) * data->visible / 100) > 0)) data->shown = 0;
                        data->end = (msg->imsg->MouseX - _mleft(obj)) * data->visible / _mwidth(obj) + data->shown;
                        /*MUI_Redraw(obj,MADF_DRAWUPDATE);*/
                        SetAttrs(obj,MUIA_Waveform_ShownStart, Samples_To_Bytes(cl ,obj ,data->shown), TAG_DONE);
                    }
                }
            }
            break;
        }
    }


    if (data->editmode == EM_ENV)
    {
        switch (msg->imsg->Class)
        {
            case IDCMP_MOUSEBUTTONS:
            {
                if (msg->imsg->Code==SELECTDOWN)
                {
                    for(UWORD n = 0; n < data->used ; n++)
                    {
                        ULONG x = _mleft(obj) + data->x[n] * (_mwidth(obj) -3) / 65535;
                        ULONG y = _mbottom(obj) -1 - data->lev[n] * (_mheight(obj) -3) / 65535;

                        if (_between(x - 2, msg->imsg->MouseX, x + 2) && _between(y - 2, msg->imsg->MouseY, y + 2))
                        {
                            data->current = n;
                            MUI_RequestIDCMP(obj,IDCMP_MOUSEMOVE);
                        }
                    }

                    data->quickdraw = QDM_ENVPNT;
                    MUI_Redraw(obj,MADF_DRAWUPDATE);
                    data->quickdraw = QDM_ALL;
                }
                else
                    MUI_RejectIDCMP(obj,IDCMP_MOUSEMOVE);
                    MUI_Redraw(obj,MADF_DRAWOBJECT);
            }
            break;

            case IDCMP_MOUSEMOVE:
            {
                LONG x, prevx;
                ULONG lev;

                if (data->current == 0)
                    data->x[0] = 0;
                else
                    if (data->current == (data->used -1))
                        data->x[data->used -1] = 65535;
                    else
                    {
                        x = (msg->imsg->MouseX - _mleft(obj)) * 65535 / (_mwidth(obj) -3);
                        prevx = data->x[data->current -1];
                        if (x < prevx) x = data->x[data->current -1];
                        if (x > data->x[data->current +1]) x = data->x[data->current +1];
                        data->x[data->current] = x;
                   }
                
                if (_between(_mtop(obj)+4, msg->imsg->MouseY, _mbottom(obj)))
                    lev = msg->imsg->MouseY;
                else
                    if (msg->imsg->MouseY < (_mtop(obj) +4))
                        lev = _mtop(obj) +4;
                    else
                        lev = _mbottom(obj);
                
                data->lev[data->current] = (_mbottom(obj) - lev +1) * 65535 / (_mheight(obj) -3);
                set(obj,MUIA_Envelope_Level,data->lev[data->current]);
                data->quickdraw = QDM_ENVPNT;
                MUI_Redraw(obj,MADF_DRAWUPDATE);
                data->quickdraw = QDM_ALL;
            }
            break;
        }
    }

}


SAVEDS ULONG mUpdate(struct IClass *cl,Object *obj,Msg msg)
{
    struct WaveData *data = INST_DATA(cl,obj);

    set(obj, MUIA_Waveform_Waveform, data->waveform);
    /*set(obj, MUIA_Waveform_Length, data->length);
    set(obj, MUIA_Waveform_ShownStart, data->shown);
    set(obj, MUIA_Waveform_Visible, data->visible);*/

    MUI_Redraw(obj,MADF_DRAWOBJECT);
    return(0);
}


SAVEDS ULONG mAddPnt(struct IClass *cl,Object *obj,Msg msg)
{
    struct WaveData *data = INST_DATA(cl,obj);

    data->used++;


    MUI_Redraw(obj,MADF_DRAWOBJECT);
    return(0);
}


SAVEDS ULONG mDelPnt(struct IClass *cl,Object *obj,Msg msg)
{
    struct WaveData *data = INST_DATA(cl,obj);

    if (data->used > 2) data->used--;


    MUI_Redraw(obj,MADF_DRAWOBJECT);
    return(0);
}


SAVEDS ASM ULONG WaveDispatcher(REG(a0) struct IClass *cl,REG(a2) Object *obj,REG(a1) Msg msg)
{
        switch (msg->MethodID)
        {
                case OM_NEW        : return(mNew      (cl,obj,(APTR)msg));
                case OM_DISPOSE    : return(mDispose  (cl,obj,(APTR)msg));
                case OM_SET        : return(mSet      (cl,obj,(APTR)msg));
                case OM_GET        : return(mGet      (cl,obj,(APTR)msg));
                case MUIM_AskMinMax: return(mAskMinMax(cl,obj,(APTR)msg));
                case MUIM_Setup    : return(mSetup    (cl,obj,(APTR)msg));
                case MUIM_Cleanup  : return(mCleanup  (cl,obj,(APTR)msg));
                case MUIM_Draw     : return(mDraw     (cl,obj,(APTR)msg));
                case MUIM_HandleInput   : return(mHandleInput(cl,obj,(APTR)msg));
                case MUIM_Waveform_Update   : return(mUpdate(cl,obj,(APTR)msg));
                case MUIM_Envelope_AddPnt   : return(mAddPnt(cl,obj,(APTR)msg));
                case MUIM_Envelope_DelPnt   : return(mDelPnt(cl,obj,(APTR)msg));
        }

        return(DoSuperMethodA(cl,obj,msg));
}

