#define TIMELINE_ICON ICON_MDI_CHART_TIMELINE
#define TIMELINE_TITLE "Timeline " TIMELINE_ICON

EDITOR_BIND(timeline, "held(CTRL)&down(3)", { ui_show(TIMELINE_TITLE, ui_visible(TIMELINE_TITLE) ^ true); });

int ui_tween(const char *label, tween_t *t) {
    if( ui_filter && ui_filter[0] ) if( !strstr(label, ui_filter) ) return 0;

    int expand_keys = label[0] == '!'; label += expand_keys;
    const char *id = label;
    if( strchr(id, '@') ) *strchr((char*)(id = (const char*)va("%s", label)), '@') = '\0';

    enum { LABEL_SPACING = 250 };
    enum { ROUNDING = 0 };
    enum { THICKNESS = 1 };
    enum { PIXELS_PER_SECOND = 60 };
    enum { KEY_WIDTH = 5, KEY_HEIGHT = 5 };
    enum { TIMELINE_HEIGHT = 25 };
    enum { MARKER1_HEIGHT = 5, MARKER10_HEIGHT = 20, MARKER5_HEIGHT = (MARKER1_HEIGHT + MARKER10_HEIGHT) / 2 };
    unsigned base_color = WHITE;
    unsigned time_color = YELLOW;
    unsigned duration_color = ORANGE;
    unsigned key_color = GREEN;

    int changed = 0;

#if 0
    // two rows with height:30 composed of three widgets
    nk_layout_row_template_begin(ui_ctx, 30);
    nk_layout_row_template_push_variable(ui_ctx, t->duration * PIXELS_PER_SECOND); // min 80px. can grow
    nk_layout_row_template_end(ui_ctx);
#endif

        char *sid = va("%s.%d", id, 0);
        uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
        for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
        ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;

    ui_label(label);

    struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
    struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx);
    bounds.y -= 30;

    struct nk_rect baseline = bounds; baseline.y += 30/2;
    baseline.x += LABEL_SPACING;
    baseline.w -= LABEL_SPACING;

    // tween duration
    {
        struct nk_rect pos = baseline;
        pos.w  = pos.x + t->duration * PIXELS_PER_SECOND;
        pos.y -= TIMELINE_HEIGHT/2;
        pos.h  = TIMELINE_HEIGHT;
        nk_stroke_rect(canvas, pos, ROUNDING, THICKNESS*2, AS_NKCOLOR(duration_color));
    }

    // tween ranges
    for(int i = 0, end = array_count(t->keyframes) - 1; i < end; ++i) {
        tween_keyframe_t *k = t->keyframes + i;
        tween_keyframe_t *next = k + 1;

        struct nk_rect pos = baseline;
        pos.x += k->t * PIXELS_PER_SECOND;
        pos.w  = (next->t - k->t) * PIXELS_PER_SECOND;
        pos.y -= TIMELINE_HEIGHT/2;
        pos.h  = TIMELINE_HEIGHT;

        char *sid = va("%s.%d", id, i);
        uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
        for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
        ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;

        struct nk_color c = nk_hsva_f(ui_hue, 0.75f, 0.8f, ui_alpha);
        nk_fill_rect(canvas, pos, ROUNDING, k->ease == EASE_ZERO ? AS_NKCOLOR(0) : c); // AS_NKCOLOR(track_color));
    }

    // horizontal line
    nk_stroke_line(canvas, baseline.x, baseline.y, baseline.x+baseline.w,baseline.y, THICKNESS, AS_NKCOLOR(base_color));

    // unit, 5-unit and 10-unit markers
    for( float i = 0, j = 0; i < baseline.w; i += PIXELS_PER_SECOND/10, ++j ) {
        int len = !((int)j%10) ? MARKER10_HEIGHT : !((int)j%5) ? MARKER5_HEIGHT : MARKER1_HEIGHT;
        nk_stroke_line(canvas, baseline.x+i, baseline.y-len, baseline.x+i, baseline.y+len, THICKNESS, AS_NKCOLOR(base_color));
    }

    // time marker
    float px = t->time * PIXELS_PER_SECOND;
    nk_stroke_line(canvas, baseline.x+px, bounds.y, baseline.x+px, bounds.y+bounds.h, THICKNESS*2, AS_NKCOLOR(time_color));
    nk_draw_symbol(canvas, NK_SYMBOL_TRIANGLE_DOWN, ((struct nk_rect){ baseline.x+px-4,bounds.y-4-8,8,8}), /*bg*/AS_NKCOLOR(0), /*fg*/AS_NKCOLOR(time_color), 0.f/*border_width*/, ui_ctx->style.font);

    // key markers
    for each_array_ptr(t->keyframes, tween_keyframe_t, k) {
        struct nk_rect pos = baseline;
        pos.x += k->t * PIXELS_PER_SECOND;

        vec2 romboid[] = {
            {pos.x-KEY_WIDTH,pos.y}, {pos.x,pos.y-KEY_HEIGHT},
            {pos.x+KEY_WIDTH,pos.y}, {pos.x,pos.y+KEY_HEIGHT}
        };

        nk_fill_polygon(canvas, (float*)romboid, countof(romboid), AS_NKCOLOR(key_color));
    }

    // keys ui
    if( expand_keys )
    for(int i = 0, end = array_count(t->keyframes); i < end; ++i) {
        tween_keyframe_t *k = t->keyframes + i;
        if( ui_collapse(va("Key %d", i), va("%s.%d", id, i))) {
            changed |= ui_float("Time", &k->t);
            changed |= ui_float3("Value", &k->v.x);
            changed |= ui_list("Ease", ease_enums(), EASE_NUM, &k->ease );
            ui_collapse_end();
        }
    }

    return changed;
}

tween_t* rand_tween() {
    tween_t demo = tween();
    int num_keys = randi(2,8);
    double t = 0;
    for( int i = 0; i < num_keys; ++i) {
        tween_setkey(&demo, t, scale3(vec3(randf(),randf(),randf()),randi(-5,5)), randi(0,EASE_NUM) );
        t += randi(1,5) / ((float)(1 << randi(0,2)));
    }
    tween_t *p = CALLOC(1, sizeof(tween_t));
    memcpy(p, &demo, sizeof(tween_t));
    return p;
}

int editor_timeline(int window_mode) {
    static array(tween_t*) tweens = 0;

    do_once {
        array_push(tweens, rand_tween());
    }

    if( editor.t == 0 )
    for each_array(tweens, tween_t*,t) {
        tween_reset(t);
    }
    else
    for each_array(tweens, tween_t*,t) {
        tween_update(t, editor.dt);
    }

    static void *selected = NULL;
    if( editor_begin(TIMELINE_TITLE, window_mode) ) {

        int choice = ui_toolbar(ICON_MDI_PLUS ";" ICON_MDI_MINUS );
        if( choice == 1 ) array_push(tweens, rand_tween());
        if( choice == 2 && selected ) {
            int target = -1;
            for( int i = 0, end = array_count(tweens); i < end; ++i ) if( tweens[i] == selected ) { target = i; break; }
            if( target >= 0 ) { array_erase_slow(tweens, target); selected = NULL; }
        }

        for each_array(tweens, tween_t*,t) {
            ui_tween(va("%s%p@%05.2fs Value: %s", t == selected ? "!":"", t, t->time, ftoa3(t->result)), t);
            if(ui_label_icon_clicked_L.x) selected = (t != selected) ? t : NULL;
        }

        editor_end(window_mode);
    }
    return 0;
}

AUTORUN {
    array_push(editor.subeditors, editor_timeline);
}