/*
(c) Copyright 1988-1994 Microsoft Corporation.

Microsoft Bars&Pipes Professional Source Code License

This License governs use of the accompanying Software.  
Microsoft hopes you find this Software useful.

You are licensed to do anything you want with the Software. 

In return, we simply require that you agree:

1.      Not to remove any copyright notices from the Software.

2.      That the Software comes "as is", with no warranties.  
        None whatsoever. This means no implied warranty of merchantability or 
        fitness for a particular purpose or any warranty of non-infringement.  
        Also, you must pass this disclaimer on whenever you distribute the Software.

3.      That we will not be liable for any of those types of damages known as indirect, 
        special, consequential, or incidental related to the Software or this License, 
        to the maximum extent the law permits. Also, you must pass this limitation of 
        liability on whenever you distribute the Software.

4.      That if you sue anyone over patents that you think may apply to the Software 
        for that person’s use of the Software, your license to the Software ends automatically.

5.      That the patent rights Microsoft is licensing only apply to the Software, 
        not to any derivatives you make.

6.      That your rights under the License end automatically if you breach this in any way.
*/

#include "/CompilerSpecific.h"
#include <intuition/intuition.h>
#include <exec/memory.h>
#include <graphics/gfxmacros.h>
#include "tools.h"
/*#include <proto/mathffp.h>*/
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
//#include <proto/mathtrans.h>
#include <math.h>
#include <string.h>
#include "all.h"

extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;
extern struct MathBase *MathBase;

#define half 0x80000040

static long sinetable[256] = {
0x0, 0xC90A753B, 0xC8FB453C, 0x96A9073D, 
0xC8BD333D, 0xFAB2693D, 0x96408A3E, 0xAF10A53E, 
0xC7C5C23E, 0xE05C183E, 0xF8CFCE3E, 0x888E933F, 
0x94A02F3F, 0xA09AE83F, 0xAC7CD73F, 0xB8442B3F, 
0xC3EF153F, 0xCF7BC93F, 0xDAE8843F, 0xE633733F, 
0xF15AEE3F, 0xFC5D2A3F, 0x839C3E40, 0x88F59B40, 
0x8E39D940, 0x93682C40, 0x987FC140, 0x9D7FD240, 
0xA2679940, 0xA7365740, 0xABEB4A40, 0xB085BA40, 
0xB504F540, 0xB9684340, 0xBDAEFA40, 0xC1D87040, 
0xC5E40440, 0xC9D11340, 0xCD9F0240, 0xD14D3F40, 
0xD4DB3340, 0xD8485340, 0xDB941A40, 0xDEBE0540, 
0xE1C59940, 0xE4AA5A40, 0xE76BD940, 0xEA09A740, 
0xEC835F40, 0xEED89E40, 0xF1090840, 0xF3144940, 
0xF4FA0C40, 0xF6BA0840, 0xF853F840, 0xF9C79E40, 
0xFB14BF40, 0xFC3B2840, 0xFD3AAD40, 0xFE132440, 
0xFEC46E40, 0xFF4E6E40, 0xFFB11040, 0xFFEC4440, 
0x80000041, 0xFFEC4440, 0xFFB11040, 0xFF4E6E40, 
0xFEC46E40, 0xFE132440, 0xFD3AAC40, 0xFC3B2840, 
0xFB14BE40, 0xF9C79E40, 0xF853F840, 0xF6BA0840, 
0xF4FA0A40, 0xF3144740, 0xF1090840, 0xEED89E40, 
0xEC835F40, 0xEA09A840, 0xE76BD940, 0xE4AA5A40, 
0xE1C59740, 0xDEBE0540, 0xDB941B40, 0xD8485340, 
0xD4DB3040, 0xD14D3D40, 0xCD9F0240, 0xC9D11040, 
0xC5E40240, 0xC1D87140, 0xBDAEF740, 0xB9684040, 
0xB504F540, 0xB085BA40, 0xABEB4740, 0xA7365340, 
0xA2679940, 0x9D7FD240, 0x987FBE40, 0x93682C40, 
0x8E39D940, 0x88F59740, 0x839C3D40, 0xFC5D233F, 
0xF15AE73F, 0xE6336C3F, 0xDAE87E3F, 0xCF7BC93F, 
0xC3EF0E3F, 0xB8442B3F, 0xAC7CCF3F, 0xA09AE03F, 
0x94A0283F, 0x888E933F, 0xF8CFBF3E, 0xE05C093E, 
0xC7C5C23E, 0xAF10A53E, 0x9640793E, 0xFAB24B3D, 
0xC8BD343D, 0x96A8E93D, 0xC8FB063C, 0xC90A773B, 
0x0, 0xC90AF8BB, 0xC8FB86BC, 0x96A909BD, 
0xC8BD53BD, 0xFAB289BD, 0x964089BE, 0xAF10A5BE, 
0xC7C5D1BE, 0xE05C27BE, 0xF8CFCEBE, 0x888E9BBF, 
0x94A037BF, 0xA09AE8BF, 0xAC7CD7BF, 0xB84432BF, 
0xC3EF1DBF, 0xCF7BD0BF, 0xDAE885BF, 0xE6337ABF, 
0xF15AEEBF, 0xFC5D2ABF, 0x839C41C0, 0x88F59EC0, 
0x8E39DCC0, 0x93682CC0, 0x987FC4C0, 0x9D7FD2C0, 
0xA2679CC0, 0xA73659C0, 0xABEB4DC0, 0xB085BDC0, 
0xB504F6C0, 0xB96846C0, 0xBDAEFAC0, 0xC1D876C0, 
0xC5E404C0, 0xC9D113C0, 0xCD9F05C0, 0xD14D3FC0, 
0xD4DB35C0, 0xD84856C0, 0xDB941FC0, 0xDEBE05C0, 
0xE1C599C0, 0xE4AA5AC0, 0xE76BD9C0, 0xEA09A9C0, 
0xEC8361C0, 0xEED8A1C0, 0xF10908C0, 0xF31449C0, 
0xF4FA0BC0, 0xF6BA09C0, 0xF853F9C0, 0xF9C79FC0, 
0xFB14C1C0, 0xFC3B28C0, 0xFD3AADC0, 0xFE1324C0, 
0xFEC46EC0, 0xFF4E6EC0, 0xFFB110C0, 0xFFEC44C0, 
0x800000C1, 0xFFEC44C0, 0xFFB110C0, 0xFF4E6EC0, 
0xFEC46DC0, 0xFE1323C0, 0xFD3AACC0, 0xFC3B28C0, 
0xFB14BFC0, 0xF9C79EC0, 0xF853F7C0, 0xF6BA07C0, 
0xF4FA09C0, 0xF31445C0, 0xF10908C0, 0xEED89EC0, 
0xEC835DC0, 0xEA09A6C0, 0xE76BD6C0, 0xE4AA56C0, 
0xE1C599C0, 0xDEBE05C0, 0xDB941AC0, 0xD84851C0, 
0xD4DB30C0, 0xD14D3AC0, 0xCD9F00C0, 0xC9D113C0, 
0xC5E402C0, 0xC1D871C0, 0xBDAEF7C0, 0xB96840C0, 
0xB504EFC0, 0xB085B7C0, 0xABEB4AC0, 0xA73657C0, 
0xA26799C0, 0x9D7FCEC0, 0x987FBEC0, 0x936825C0, 
0x8E39D6C0, 0x88F59BC0, 0x839C3AC0, 0xFC5D23BF, 
0xF15AE7BF, 0xE6336CBF, 0xDAE876BF, 0xCF7BBABF, 
0xC3EF15BF, 0xB8442BBF, 0xAC7CCFBF, 0xA09AE0BF, 
0x94A028BF, 0x888E8BBF, 0xF8CFAFBE, 0xE05C18BE, 
0xC7C5C2BE, 0xAF1095BE, 0x96407ABE, 0xFAB249BD, 
0xC8BD13BD, 0x96A8C8BD, 0xC8FB06BC, 0xC90A75BB, 
};

static knob_sine(index)

register unsigned short index;

{
    return(sinetable[index >> 8]);
}

static knob_cosine(index)

register unsigned short index;

{
    index += 16384;
    return(sinetable[index >> 8]);
}


static unsigned long knob_sqrt(r)

register unsigned long r;

{
    static unsigned short rootable[34] = { 
        1,1,2,3,4,6,8,11,16,23,32,45,64,90,128,181,256,362,512,724,1024,
        1448,2048,2896,4096,5793,8192,11585,16384,23170,32768,46341,46341
    };
    register short index;
    register unsigned long power;
    r = SPFix(r);
    power = r;
    if (r > 3) {
        for (index = 0;power;index++) power = power >> 1;
        power = rootable[index];
        if (r > 65535) {
            if (power) power = r/power + power;
            else power = 1;
            power = power >> 1;
            if (power) power = r/power + power;
            else power = 1;
            power = power >> 1;
        }
        else {
            if (power) power = IDivU(r,power) + power;
            else power = 1;
            power = power >> 1;
            if (power) power = IDivU(r,power) + power;
            else power = 1;
            power = power >> 1;
            if (power) power = IDivU(r,power) + power;
            else power = 1;
            power = power >> 1;
        }
    }
    else power = 1;
    return(power);
}

//void knob_job();

/*struct Knob *KnobGadgets(window,knoblist)

register struct Window *window;
register struct Knob *knoblist;

{
    register short x,y;
    register long radius, xd, yd;
    x = window->MouseX;
    y = window->MouseY;
    for (;knoblist;knoblist = knoblist->NextKnob) {
        xd = x - knoblist->XCenter;
        xd = SPFlt(IMulS(xd,xd));
        if (knoblist->AY)
            yd = IDivS(IMulS(y - knoblist->YCenter,knoblist->AX),knoblist->AY);
        else yd = 1;
        yd = SPFlt(IMulS(yd,yd));
        radius = knob_sqrt(SPAdd(yd,xd));
        if (radius <= knoblist->ArrowRadius) {
            if (knob_calc(window,knoblist,x,y,1)) break;
        }
    }
    if (knoblist) knob_job(window,knoblist);
    return(knoblist);
}*/

/* long max(long a, long b)
{
    if (a<b) return b;
    if (a>b) return a;
}

long min(long a, long b)
{
    if (a<b) return a;
    if (a>b) return b;
}  */

//extern long knob_arccos(long);

knob_angle(knob,x,y)

register struct Knob *knob;
register long x,y;

{
    register long r;
    register long ox = knob->XCenter;
    register long oy = knob->YCenter;
//added A.Faust
    register float a,b,c,sine,cosine,anglea,angleb,angle,gradconv;
    gradconv=3.1415927/180.0; //RAD<>GRAD

    b = (float)abs(x-ox); //absolut x
    c = (float)abs(y-oy); //absolut y
    a = sqrt(b*b+c*c);    //calculate the 3.side in the 90° triangle
    sine = c/a;           //sin of the rotationsangle
    cosine = b/a;         //cos of the rotationsangle

    //calculation of the real angle in GRD
    anglea = (acos(sine))/gradconv;
    angleb = (acos(cosine))/gradconv;

    //adapdation for the whole circle
    if((x-ox)<=0 && (y-oy)>=0) angle = anglea;
    if((x-ox)<=0 && (y-oy)<=0) angle = 90+angleb;
    if((x-ox)>0  && (y-oy)<=0) angle = 180+anglea;
    if((x-ox)>0  && (y-oy)>0)  angle = 270+angleb;

    //calculation for the 0xFFFF(65535) boundary
    r =(long)(angle*182);
// endadd

// added Kjetil
// knob_arccos is not available, so this one will do for now:
// return min(0xffff,max(0,(x-ox+160)*0xffff/320));
// endadd
// original
    /* x = x - ox;
    if (knob->AY) y = IDivS(IMulS(y - oy,knob->AX),knob->AY);
    else y = 1;
    ox = SPFlt(IMulS(x,x));
    oy = SPFlt(IMulS(y,y));
    r = SPFlt(knob_sqrt(SPAdd(ox,oy)));
    if (x) {
                r = SPDiv(r,SPFlt(x));
                r = knob_arccos(r);
                r = acos(r);
                r = SPFix(SPMul(0xA2F9834E,r));
                if (y < 0) r = 65536 - r;
    }
    else if (y > 0) r = 16384;
    else r = 49152;
    r = (r + 16384 + knob->Offset) & 0xFFFF;*/
    return(r); 

}

//void knob_drawknob();

void DrawArrow(rp,knob)

register struct RastPort *rp;
register struct Knob *knob;

{
    register unsigned long value, range;
    if (knob->Flags & KNOB_CALCVALUE) {
        if (knob->Range) {
            value = knob->Value;
            value = value << 16;
            if (knob->Range) value = IDivU(value,knob->Range);
            else value = knob->Value;
            if (knob->Flags & KNOB_RANGED) {
                range = knob->StopAngle - knob->StartAngle;
                value = IMulU(value,range);
                value = value >> 16;
                value += knob->StartAngle;
            }
            else range = 65536;
            range = (range / knob->Steps) >> 1;
            knob->Angle = value + range;
        }
        else knob->Angle = knob->Value;
    }
    knob_drawknob(rp,knob);
}
  

void knob_drawknob(rp,knob)

register struct RastPort *rp;
register struct Knob *knob;

{
    register long x, y;
    register long angle;
    register long radius;
    angle = (knob->Angle + 49152);
    angle -= knob->Offset;
    angle &= 0xFFFF;
    radius = SPFlt(knob->ArrowRadius);
    x = SPFix(SPMul(radius,knob_cosine(angle)));
    y = SPFix(SPMul(radius,knob_sine(angle)));
    if (knob->AX) y = IDivS(IMulS(y,knob->AY),knob->AX);
    else y = 1;
    if (knob->Flags & KNOB_COMPLEMENT) SetDrMd(rp,COMPLEMENT);
    else {
        SetDrMd(rp,JAM1);
        if (knob->OX) {
            SetAPen(rp,knob->FillColor);
            Move(rp,knob->XCenter,knob->YCenter);
            Draw(rp,knob->OX,knob->OY);
        }
        SetAPen(rp,knob->ArrowColor);
    }
    Move(rp,knob->XCenter,knob->YCenter);
    knob->OX = knob->XCenter + x;
    knob->OY = knob->YCenter + y;
    Draw(rp,knob->OX,knob->OY);
}


unsigned long knob_steptoangle(knob,step)

register struct Knob *knob;
register unsigned short step;

{
    register unsigned long fullrange, result;
    if (knob->Flags & KNOB_RANGED) 
        fullrange = knob->StopAngle - knob->StartAngle;
    else fullrange = 65536;
    result = step * fullrange;
    result += fullrange >> 1;
    if (knob->Steps) result = IDivU(result,knob->Steps);
    else result = 0;
    result += knob->StartAngle;
    result &= 0xFFFF;
    return(result);
}

long knob_angletostep(knob,angle)

register struct Knob *knob;
register unsigned long angle;

{
    register unsigned long fullrange, result;
    if (knob->Flags & KNOB_RANGED) {
        if (angle < knob->StartAngle) return(-1);
        if (angle > knob->StopAngle) return(-1);
        angle -= knob->StartAngle;
        fullrange = knob->StopAngle - knob->StartAngle;
    }
    else fullrange = 65535;
    if (knob->Steps) {
        result = fullrange / knob->Steps;
        result = result >> 1;
        if (angle < result) result = 0;
        else result = (angle - result) * knob->Steps;
        if (fullrange) result = IDivU(result,fullrange);
        else result = 0;
    }
    else result = angle;
    return((long)result);
}


/*void DrawKnobs(window,knob)

register struct Window *window;
register struct Knob *knob;

{
    register long x,y;
    register struct Knob *knoblist = knob;
    register unsigned short i;
    register long radius;
    register long ratio;
    unsigned short ax = 54;
    unsigned long ay = 24;
    for (;knoblist;knoblist = knoblist->NextKnob) {
        knoblist->AX = ax;
        knoblist->AY = ay;
        if ((mode & KNOB_RANGED) && 
            (knoblist->StartAngle == knoblist->StopAngle)) {
            if (knoblist->StopAngle == 65536) knoblist->StartAngle--;
            else knoblist->StopAngle++;
        }
        knoblist->OX = knoblist->XCenter;
        knoblist->OY = knoblist->YCenter;
    }
    for (;knob;knob = knob->NextKnob) {
        if (knob->KnobRadius < 1) knob->KnobRadius = 1;
        if (knob->Flags & KNOB_DRAW) {
            DrawArrow(window->RPort,knob);
        }
    }
}*/


knob_calc(window,knob,x,y,testing)

register struct Window *window;
register struct Knob *knob;
short x,y,testing;

{
    register unsigned long value;
    register long angle;
    register long step;
    register long start, stop;
    angle = knob_angle(knob,x,y);
    step = knob_angletostep(knob,angle);
    if (step == -1) {
        if (testing) return(0);
        if ((knob->StartAngle >= angle) || (knob->StopAngle <= angle)) {
            stop = knob->StopAngle;
            if (angle < knob->StartAngle) angle += 65536;
            start = knob->StartAngle + 65536;
            start = start - angle;
            stop = angle - stop;
            if (stop < start) step = knob->Steps - 1;
            else step = 0;
            angle &= 0xFFFF;
        }
    }
    if (step != knob->OS) {
        knob->OS = step;
        knob_drawknob(window->RPort,knob);
        knob->Angle = knob_steptoangle(knob,step);
        if (knob->Flags & KNOB_CALCVALUE) {
            if (knob->Range) {
                value = knob->Angle;
                if (knob->Flags & KNOB_RANGED) {
                    if (value < knob->StartAngle) value = 0;
                    else value = value - knob->StartAngle;
                    value = value << 16;
                    if (knob->StopAngle > knob->StartAngle)
                        value = IDivU(value,knob->StopAngle - knob->StartAngle);
                    else value = 0;
                }
                value = IMulU(value,knob->Range);
                value = value >> 16;
                knob->Value = value;
            }
            else knob->Value = knob->Angle;
        }
        knob_drawknob(window->RPort,knob);
        if (knob->UpdateRoutine) 
            (*knob->UpdateRoutine)(window,knob);
    }
    return(1);
}

void knob_job(window,knob)

register struct Window *window;
register struct Knob *knob;

{
    register struct IntuiMessage *message;
    register short x,y;
    register long code, class;
    register long reportmouse = window->Flags & REPORTMOUSE;
    register long oldflags = window->IDCMPFlags;
    ModifyIDCMP(window,MOUSEBUTTONS+MOUSEMOVE+GADGETUP);
    window->Flags |= REPORTMOUSE;
    for (;;) {
        do {
            message = (struct IntuiMessage *) 
                GetMsg(window->UserPort);
            if (message) {
                if (message->Class == MOUSEBUTTONS) break;
                if (message->Class == GADGETUP) break;
                ReplyMsg((struct Message *)message);
            }
        } while (message);
        while (!message) {
            WaitPort(window->UserPort);
            message = (struct IntuiMessage *) GetMsg(window->UserPort);
        }
        class = message->Class;
        code = message->Code;
        x = message->MouseX;
        y = message->MouseY;
        ReplyMsg((struct Message *)message);
        if (class == MOUSEMOVE) knob_calc(window,knob,x,y,0);
        else if ((class == MOUSEBUTTONS) && (code == SELECTUP)) break;
        else if (class == GADGETUP) break;
    }
    ModifyIDCMP(window,oldflags);
    if (!reportmouse) window->Flags &= ~REPORTMOUSE;
}    
