#define TURTLE_SIZE                                             4
#define TURTLE_SPEED_STEP                       2

#define ANGLES  35
F64 angles[ANGLES] = {

        -2 * pi / 1,  -2 * pi / 2,  -2 * pi / 3,  -2 * pi / 4, -2 * pi / 5, 
        -2 * pi / 6,  -2 * pi / 8,  -2 * pi / 9,  -2 * pi / 10, 
        -2 * pi / 12, -2 * pi / 15, -2 * pi / 18, -2 * pi / 20, 
        -2 * pi / 24, -2 * pi / 30, -2 * pi / 36, -2 * pi / 40, 
        0, 
        2 * pi / 40, 2 * pi / 36, 2 * pi / 30, 2 * pi / 24, 
        2 * pi / 20, 2 * pi / 18, 2 * pi / 15, 2 * pi / 12, 
        2 * pi / 10, 2 * pi / 9,  2 * pi / 8,  2 * pi / 6, 
        2 * pi / 5,  2 * pi / 4,  2 * pi / 3,  2 * pi / 2, 2 * pi / 1
};

class Turtle
{
        F64                             x, y, z, speed, theta, w;
        I64                             dtheta_idx;
        CColorROPU16    edge, middle;
        Bool                    ends, first;

} tt;

U0 TurtlePlot(CDC *dc, Turtle *t, CColorROPU16 edge, CColorROPU16 middle)
{
        F64 w = t->w / 2.0 - 1;

        if (w < 0)
                w = 0;
        dc->color = middle;
        GrLine3(dc, t->x + w * Cos(t->theta + pi / 2), t->y + w * Sin(t->theta + pi / 2), t->z, 
                                t->x + w * Cos(t->theta - pi / 2), t->y + w * Sin(t->theta - pi / 2), t->z);
        w = t->w / 2.0;
        dc->color = edge;
        GrPlot3(dc, t->x + w * Cos(t->theta + pi / 2), t->y + w * Sin(t->theta + pi / 2), t->z);
        GrPlot3(dc, t->x + w * Cos(t->theta - pi / 2), t->y + w * Sin(t->theta - pi / 2), t->z);
}

U0 TurtleMicroMove(Turtle *t, F64 dt)
{
        t->x += dt * t->speed * Cos(t->theta);
        t->y += dt * t->speed * Sin(t->theta);
        t->theta = Wrap(t->theta + dt * angles[t->dtheta_idx]);
}

U0 TurtleEnd(CDC *dc, Turtle *t, CColorROPU16 edge, CColorROPU16 middle, F64 theta)
{
        F64             r, x, y2;
        Turtle  t2;

        if (r = t->w)
        {
                MemCopy(&t2, t, sizeof(Turtle));        //Save
                x = 0;
                while (TRUE)
                {
                        t->x += 1 / r *Cos(theta);
                        t->y += 1 / r * Sin(theta);
                        x += 1 / r;
                        y2 = r * r - 4 * x * x;
                        if (y2 >= 0)
                        {
                                t->w = Sqrt(y2);
                                TurtlePlot(dc, t, edge, middle);
                        }
                        else
                                break;
                }
                MemCopy(t, &t2, sizeof(Turtle));
        }
}

U0 TurtleMove(CDC *dc, Turtle *t, CColorROPU16 edge, CColorROPU16 middle)
{
        I64 i, l = 16 * AbsI64(t->w + 1) * AbsI64(t->speed + 1);

        if (t->ends && t->first)
                TurtleEnd(dc, t, edge, middle, t->theta + pi);
        t->first = FALSE;

        for (i = 0; i < l; i++)
        {
                TurtleMicroMove(t, 1.0 / l);
                TurtlePlot(dc, t, edge, middle);
        }
        if (t->ends)
                TurtleEnd(dc, t, edge, middle, t->theta);
}

U0 TurtleInit(Turtle *t)
{
        MemSet(t, 0, sizeof(Turtle));
        t->x            = Fs->pix_width  >> 1;
        t->y            = Fs->pix_height >> 1;
        t->z            = 5;
        t->edge         = BLACK;
        t->middle       = YELLOW;
        t->dtheta_idx   = ANGLES / 2;
        t->first        = TRUE;
        t->ends         = TRUE;
}

U0 DrawIt(CTask *, CDC *dc)
{
        Turtle t2;

        MemCopy(&t2, &tt, sizeof(Turtle));
        GrPrint(dc, 0, 0, "Layer:%f Speed:%f theta:%5.1f dtheta:%5.1f Width:%f", 
                        tt.z, tt.speed, tt.theta * 180 / pi, angles[tt.dtheta_idx] * 180 / pi, tt.w);

        TurtleMove(dc, &t2, RED, LTRED);
        dc->color  = LTRED;
        GrLine(dc,      t2.x + TURTLE_SIZE * Cos(t2.theta + pi / 2), t2.y + TURTLE_SIZE * Sin(t2.theta + pi / 2), 
                                t2.x + TURTLE_SIZE * Cos(t2.theta - pi / 2), t2.y + TURTLE_SIZE * Sin(t2.theta - pi / 2));

        GrLine(dc,      t2.x + TURTLE_SIZE * Cos(t2.theta + pi / 2), t2.y + TURTLE_SIZE * Sin(t2.theta + pi / 2), 
                                t2.x + TURTLE_SIZE * Cos(t2.theta),                     t2.y + TURTLE_SIZE * Sin(t2.theta));

        GrLine(dc,      t2.x + TURTLE_SIZE * Cos(t2.theta - pi / 2), t2.y + TURTLE_SIZE * Sin(t2.theta - pi / 2), 
                                t2.x + TURTLE_SIZE * Cos(t2.theta),                     t2.y + TURTLE_SIZE * Sin(t2.theta));
}

U0 SetMenu()
{
        I64                     i;
        U8                      buf[STR_LEN];
        CMenuEntry *tmpse;

        for (i = 0; i <= 9; i++)
        {
                StrPrint(buf, "Settings/Layer%d", i);
                if (tmpse = MenuEntryFind(Fs->cur_menu, buf))
                {
                        if (i == tt.z)
                                tmpse->checked = TRUE;
                        else
                                tmpse->checked = FALSE;
                }
        }
        if (tmpse = MenuEntryFind(Fs->cur_menu, "Settings/Ends"))
        {
                if (tt.ends)
                        tmpse->checked = TRUE;
                else
                        tmpse->checked = FALSE;
        }
}

U0 Lattice()
{
        Bool aim = FALSE;
        I64  arg1, arg2;
        CDC *dc = DCAlias;

        DCDepthBufAlloc(dc);
        MenuPush(       "File {"
                                "  Abort(,CH_SHIFT_ESC);"
                                "  Exit(,CH_ESC);"
                                "}"
                                "Play {"
                                "  Restart(,'\n');"
                                "  Step(,CH_SPACE);"
                                "  Accelerator(,,SC_CURSOR_UP);"
                                "  Break(,,SC_CURSOR_DOWN);"
                                "  Left(,,SC_CURSOR_LEFT);"
                                "  Right(,,SC_CURSOR_RIGHT);"
                                "}"
                                "Settings {"
                                "  Color(,'c');"
                                "  Wider(,'+');"
                                "  Narrower(,'-');"
                                "  Ends(,'e');"
                                "  Layer0(,'0');"
                                "  Layer1(,'1');"
                                "  Layer2(,'2');"
                                "  Layer3(,'3');"
                                "  Layer4(,'4');"
                                "  Layer5(,'5');"
                                "  Layer6(,'6');"
                                "  Layer7(,'7');"
                                "  Layer8(,'8');"
                                "  Layer9(,'9');"
                                "}"
                                );
        SettingsPush; //See SettingsPush
        AutoComplete;
        WinBorder;
        WinMax;
        DocCursor;
        DocClear;
        TurtleInit(&tt);
        SetMenu;
        Fs->win_inhibit = WIG_TASK_DEFAULT - WIF_FOCUS_TASK_MENU - WIF_SELF_FOCUS - WIF_SELF_GRAB_SCROLL;
        Fs->draw_it             = &DrawIt;
        try
        {
                while (TRUE)
                {
                        switch (MessageGet(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN  | 1 << MESSAGE_MS_L_DOWN|
                                                                                         1 << MESSAGE_MS_R_DOWN | 1 << MESSAGE_MS_R_UP | 1 << MESSAGE_MS_MOVE))
                        {
                                case MESSAGE_MS_L_DOWN:
                                        tt.first = TRUE;
                                        tt.x     = arg1;
                                        tt.y     = arg2;
                                        break;

                                case MESSAGE_MS_R_DOWN:
                                        aim = TRUE;
                                        tt.theta = Arg(arg1 - tt.x, arg2 - tt.y);
                                        break;

                                case MESSAGE_MS_MOVE:
                                        if (aim)
                                                tt.theta = Arg(arg1 - tt.x, arg2 - tt.y);
                                        break;

                                case MESSAGE_MS_R_UP:
                                        tt.theta = Arg(arg1 - tt.x, arg2 - tt.y);
                                        aim = FALSE;
                                        break;

                                case MESSAGE_KEY_DOWN:
                                        switch (arg1)
                                        {
                                                case 0:
                                                        switch (arg2.u8[0])
                                                        {
                                                                case SC_CURSOR_LEFT:
                                                                        if (tt.dtheta_idx)
                                                                                tt.dtheta_idx--;
                                                                        break;

                                                                case SC_CURSOR_RIGHT:
                                                                        if (tt.dtheta_idx < ANGLES - 1)
                                                                                tt.dtheta_idx++;
                                                                        break;

                                                                case SC_CURSOR_UP:
                                                                        tt.speed += TURTLE_SPEED_STEP;
                                                                        break;

                                                                case SC_CURSOR_DOWN:
                                                                        if (tt.speed >= TURTLE_SPEED_STEP)
                                                                                tt.speed -= TURTLE_SPEED_STEP;
                                                                        break;
                                                        }
                                                        break;

                                                case '0'...'9':
                                                        tt.z = arg1 - '0';
                                                        SetMenu;
                                                        break;

                                                case 'c':
                                                        tt.middle = PopUpColor("Mid Color\n\n");
                                                        tt.edge   = PopUpColor("Edge Color\n\n");
                                                        break;

                                                case 'e':
                                                        tt.ends = !tt.ends;
                                                        break;

                                                case '+':
                                                        tt.w++;
                                                        break;

                                                case '-':
                                                        if (tt.w)
                                                                tt.w--;
                                                        break;

                                                case '\n':
                                                        DCFill(dc);
                                                        TurtleInit(&tt);
                                                        SetMenu;
                                                        break;

                                                case CH_ESC:
                                                case CH_SHIFT_ESC:
                                                        goto lt_done;

                                                case CH_SPACE:
                                                        TurtleMove(dc, &tt, tt.edge, tt.middle);
                                                        break;
                                        }
                        }
                }
lt_done:
                MessageGet(,, 1 << MESSAGE_KEY_UP);
        }
        catch
                PutExcept;
        SettingsPop;
        DCFill(dc);
        DCDel(dc);
        MenuPop;
}

Lattice;