/****************************************************************************/
/*                                                                          */
/*                                ColorLine                                 */
/*  Programmino didattico sull'uso di alcune funzioni grafiche dell'Amiga   */
/*               Realizzato per AMAZING.1 da Eugenio Ciceri                 */
/*                 Basato su un'idea di Roberto Montaruli                   */
/*                                                                          */
/* NB: Potete farne cio' che volete, ma se ci fosse da lucrare (cosa di cui */
/*   dubito fortemente) avvertitemi che voglio partecipare alla festa...    */
/*                                                                          */
/****************************************************************************/

#include "intuition/intuition.h"
#include "graphics/gfxbase.h"
#include "graphics/gfxmacros.h"
#include "hardware/dmabits.h"
#include "hardware/custom.h"
#include "hardware/blit.h"
#include "exec/exec.h"
#include "exec/memory.h"

struct UCopList *ucl;

extern struct Custom custom;

#define LIN 15          /* elementi coda */

long GfxBase;
long IntuitionBase;
struct IntuiMessage *m1;
struct Message *GetMsg();
struct Window *OpenWindow();
struct Window *w;
struct Screen *OpenScreen();
struct Screen *s;
struct RastPort *rp;
struct ViewPort *vp;

struct NewScreen ns =   /* Definizione schermo */
{
    0,0,                /* Posizione XY dello schermo */
    640,256,4,          /* Larghezza, altezza e profondita' */
    1,0,                /* Dettagli relativi alla penna */
    HIRES,              /* Schermo ad alta risoluzione */
    CUSTOMSCREEN,       /* Tipo di schermo */
    NULL,
    "ColorLine Screen", /* Titolo che appare sullo schermo */
    NULL,
    NULL
};

struct NewWindow nw =       /* Definizione finestra */
{
    0,0,                    /* Posizione XY della finestra */
    640,256,                /* Dimensioni della finestra */
    2,1,                    /* Dettagli relativi alla penna */
    CLOSEWINDOW | NEWSIZE,  /* Flags IDCMP */
    ACTIVATE | WINDOWDEPTH | WINDOWDRAG | WINDOWCLOSE | GIMMEZEROZERO |
        WINDOWSIZING,       /* Caratteristiche della finestra */
    NULL,
    NULL,
    "ColorLine per AMAZING",/* Titolo che appare nella finestra */
    NULL,                   /* Puntatore allo schermo (usato piu' avanti) */
    NULL,
    50,50,                  /* Dimensioni minime della finestra */
    640,256,                /* Dimensioni massime della finestra */
    CUSTOMSCREEN            /* Tipo di schermo su cui agganciarsi */
};

OpenField()     /* Prepara il campo grafico su cui operare */
{
    /* Apre le librerie o esce se non puo' */
    if ((GfxBase=OpenLibrary("graphics.library",0)) == NULL)
    {
        printf("OpenField: Non posso aprire la graphics.library\n");
        exit(-1);
    }

    if ((IntuitionBase=OpenLibrary("intuition.library",0)) == NULL) 
    {
        printf("OpenField: Non posso aprire la intuition.library\n");
        CloseLibrary(GfxBase);
        exit(-2);
    }

    if ((s=OpenScreen(&ns))==NULL)    /* Apre lo schermo */
    {
        printf("OpenField: Non posso aprire lo schermo\n");
        CloseLibrary(IntuitionBase);
        CloseLibrary(GfxBase);
        exit(-3);
    }
    
    nw.Screen = s;          /* Aggancia la finestra allo schermo */

    if((w=OpenWindow(&nw))==NULL)    /* Apre la finestra */
    {
        printf("OpenField: Non posso aprire la finestra\n");
        CloseLibrary(IntuitionBase);
        CloseLibrary(GfxBase);
        CloseScreen(s);
        exit(-4);
    }

    rp = w->RPort;          /* Preleva il puntatore al RastPort */
    vp = &s->ViewPort;      /* e quello alla ViewPort */

    return(0);
}

CloseField()    /* Libera la memoria occupata dalla grafica e chiude le
                librerie */
{
    CloseWindow(w);
    CloseScreen(s);
    CloseLibrary(GfxBase);
    CloseLibrary(IntuitionBase);

    return(0);
}

Copper()    /* Genera una lista di istruzioni per il Copper */
{
    int j;
    int *dspins, *sprins, *clrins;
    
    /* Riserva spazio per la lista di istruzioni */
    if ((ucl=(struct UCopList *)AllocMem(sizeof(struct UCopList),
        MEMF_PUBLIC | MEMF_CLEAR))==NULL)
    {
        printf("Copper: non c'e' abbastanza memoria per UCopList.\n");
        CloseField();
        exit(-5);
    }
    
    for (j=0; j<16; j++)
    {
        /* Aspetta che il pennello video raggiunga la (j*16)ma riga
        alla colonna 0 */
        CWAIT (ucl, j*16, 0);
        
        /* Mette il colore (j*16) nel registro 0 */
        CMOVE (ucl, custom.color[0], j*256);
    }
    
    /* Termina la lista delle istruzioni */
    CEND (ucl);
    
    /* Salva le liste del Copper */
    dspins=vp->DspIns; sprins=vp->SprIns; clrins=vp->ClrIns;
    
    /* Disabilita il multitasking */
    Forbid();
    
    /* Nasconde le liste alla FreeVPortCopLists */
    vp->DspIns=vp->SprIns=vp->ClrIns=0;
    
    /* Libera la memoria occupata dalle liste del Copper */
    FreeVPortCopLists(vp);

    /* Ripristina i puntatori alle liste del Copper */
    vp->DspIns=dspins; vp->SprIns=sprins; vp->ClrIns=clrins;

    /* Aggancia la lista generata alla ViewPort in uso */
    vp->UCopIns=ucl;
    
    /* Riabilita il multitasking */
    Permit();

    /* Rigenera lo schermo utilizzando anche le nuove istruzioni */
    RethinkDisplay();
    
    return(0);
}

ColorLine()     /* Routine di tracciamento linee */
{
    int x0, x1, y0, y1, l, col, mx, my, lx, ly, j, k;
    int qr, qg, qb, sr, sg, sb, g, b;
    int dx0, dy0, dx1, dy1;
    int x[LIN][2];
    int y[LIN][2];

    qr=qg=qb=0; /* Quantita' di Rosso, Verde e Blu */
    sr=sg=sb=1; /* Flags rosso, verde e blu */

    l=0;        /* n' linee iniziali */
    col=10;     /* registro colore */
    x0=10;      /* coordinate d'ingresso punto 0 */
    y0=10;
    x1=10;      /* coordinate d'ingresso punto 1 */
    y1=10;
    dx0=3;      /* velocita' orizzontale punto 0 */
    dy0=10;     /* velocita' verticale punto 0 */
    dx1=4;      /* velocita' orizzontale punto 1 */
    dy1=7;      /* velocita' verticale punto 1 */
    lx=0;       /* limite minimo coordinata x */
    mx=320;     /* limite massimo coordinata x */
    ly=0;       /* limite minimo coordinata y */
    my=256;     /* limite massimo coordinata y */

    /* Azzeramento array coordinate linee da cancellare */
    for (j=0; j<LIN; j++)
    {
        for (k=0; k<2; k++)
        {
            x[j][k]=0;
            y[j][k]=0;
        }
    }

    /* ciclo principale */
    do
    {
        /* ciclo d'attesa (ottimo se si lavora in FAST MEMORY, altrimenti
        occorre dimezzare; sarebbe meglio poter controllare da joy...) */
        for (j=0; j<1000; j++)
            ;

        /* Cancellazione della l-esima linea */
        /* (Traccia una linea di colore 0 prendendo i dati dei vertici
        dall'array di cancellazione) */
        Line(x[l][0],y[l][0],x[l][1],y[l][1],0);

        /* Memorizza i vertici per il prossimo ciclo di cancellazione */
        x[l][0]=x0;
        y[l][0]=y0;
        x[l][1]=x1;
        y[l][1]=y1;

        /* Calcola le nuove posizioni dei vertici
        in base alle loro velocita' */
        x0+=dx0;
        y0+=dy0;
        x1+=dx1;
        y1+=dy1;

        /* Modifica i limiti massimi in base alle dimensioni della finestra */
        mx = (w->Width);
        my = (w->Height);

        /* Segue una serie di controlli sui limiti delle coordinate */
        /* (Se un punto ha superato i limiti della finestra ne modifica la
        coordinata e cambia segno alla sua velocita') */
        if (x0>mx)
        {
            x0=(2*mx)-x0;
            dx0=-dx0;
        }

        if (x1>mx)
        {
            x1=(2*mx)-x1;
            dx1=-dx1;
        }

        if (x0<lx)
        {
            x0=(2*lx)-x0;
            dx0=-dx0;
        }

        if (x1<lx)
        {
            x1=(2*lx)-x1;
            dx1=-dx1;
        }

        if (y0>my)
        {
            y0=(2*my)-y0;
            dy0=-dy0;
        }

        if (y1>my)
        {
            y1=(2*my)-y1;
            dy1=-dy1;
        }

        if (y0<ly)
        {
            y0=(2*ly)-y0;
            dy0=-dy0;
        }

        if (y1<ly)
        {
            y1=(2*ly)-y1;
            dy1=-dy1;
        }

        /* Modifica il registro del colore con i valori dati */
        SetRGB4(vp, col, qr, qg, qb);

        /* Traccia una linea tra i vertici calcolati con il colore appena
        settato */
        Line(x0, y0, x1, y1, col);

        /* Se ha tracciato tutte le linee della coda reinizializza il
        contatore */
        if (++l>LIN-1)
            l=0;

        b=0;    /* flag attivazione modifica colore blu */
        g=0;    /* flag attivazione modifica colore verde */

        qr+=sr; /* modifica la quantita' di rosso */

        /* Se la quantita' di rosso ha raggiunto il massimo cambia segno al
        modificatore del rosso ed abilita la modifica del verde */
        if (qr>14)
        {
            sr=-1;
            g=1;
        }

        /* Se la quantita' di rosso ha raggiunto il minimo cambia segno al
        modificatore del rosso ed abilita la modifica del verde */
        if (qr<1)
        {
            sr=1;
            g=1;
        }

        /* Se e' abilitato modifica la quantita' di verde */
        if (g)
        {
            qg+=sg;

            /* Se la quantita' di verde ha raggiunto il massimo cambia segno
            al modificatore del verde ed abilita la modifica del blu */
            if (qg>14)
            {
                sg=-1;
                b=1;
            }

            /* Se la quantita' di verde ha raggiunto il minimo cambia segno
            al modificatore del verde ed abilita la modifica del blu */
            if (qg<1)
            {
                sg=1;
                b=1;
            }
        }

        /* Se e' abilitato modifica la quantita' di blu */
        if (b)
        {
            qb+=sb;

            /* Se la quantita' di blu ha raggiunto il massimo o il minimo
            cambia segno al modificatore del blu */
            if (qb>14)
                sb=-1;
            if (qb<1)
                sb=1;
        }

    }while (!WannaClose()); /* Esegue fino a richiesta di uscita */

    return(0);
}

WannaClose()    /* Ritorna TRUE se c'e' stata richiesta di uscita */
{
    int Close = 0;

    /* Se c'e' stato un segnale */
    if (1<<w->UserPort->mp_SigBit)
    {
        /* preleva il messaggio */
        while((m1 = (struct IntuiMessage *)GetMsg(w->UserPort)))
        {
            /* e risponde TRUE se si tratta della richiesta di uscita */
            if (m1->Class == CLOSEWINDOW)
                Close = 1;

            /* Restituisce il messaggio */
            ReplyMsg(m1);
        }
    }

    return(Close);
}

Line(x1, y1, x2, y2, col)   /* Traccia una linea di colore col tra i punti
                            (x1,y1) e (x2,y2) */
int x1, y1, x2, y2, col;
{
    SetAPen(rp, col);       /* Penna colore col */
    Move(rp, x1, y1);       /* Muove la penna alle coordinate iniziali */
    Draw(rp, x2, y2);       /* Traccia una linea fino alle coordinate
                            finali */
    return(0);
}

main()      /* Routine principale */
{
    OpenField();        /* Inizializzazione */
    Copper();           /* Istruzioni al Copper */
    ColorLine();        /* Esecuzione */
    CloseField();       /* Chiusura */

    return(0);
}
