#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;