<script>
    import { onMount } from 'svelte';
    import { writable, derived } from 'svelte/store';
    import { Matrix } from 'ml-matrix';

    import Chaos from './Chaos.svelte';
    import Point from './Point.svelte';

    // Page dimensions
    let pageWidth;
    let pageHeight;

    // Controls dimensions
    const controlsWidth = 200;

    // Render dimensions
    $: renderWidth = pageWidth - controlsWidth;
    $: renderHeight = pageHeight;

    // Number of points
    let count = 3;

    // Whether controls are shown
    let controls = true;

    // Fraction
    const ratios = writable([]);

    // Bounding points of the fractal
    const points = writable([]);

    // Center of the fractal
    const center = derived(points, ($points, set) =>
    {
        set(Matrix.div(
            $points.reduce(
                (prev, point) => Matrix.add(prev, point),
                Matrix.zeros(3, 1)
            ),
            $points.length
        ));
    });

    // Derived IFS
    const ifs = derived([ratios, points], ([$ratios, $points], set) =>
    {
        set($points.map((point, index) => [
            1,
            new Matrix([
                [$ratios[index], 0, point.get(0, 0) * (1 - $ratios[index])],
                [0, $ratios[index], point.get(1, 0) * (1 - $ratios[index])],
                [0, 0, 1]
            ])
        ]))
    });

    const myifs = [
        [1, new Matrix([
            [0, 0, 0],
            [0, 0.16, 0],
            [0, 0, 1]
        ])],
        [85, new Matrix([
            [0.85, 0.04, 0],
            [-0.04, 0.85, 160],
            [0, 0, 1]
        ])],
        [7, new Matrix([
            [0.20, -0.26, 0],
            [0.23, 0.22, 160],
            [0, 0, 1]
        ])],
        [7, new Matrix([
            [-0.15, 0.28, 0],
            [0.26, 0.24, 44],
            [0, 0, 1]
        ])]
    ];

    for (let f of myifs)
    {
        /* f[1] = new Matrix([ */
        /*     [1, 0, 100], */
        /*     [0, 1, 100], */
        /*     [0, 0, 1] */
        /* ]).mmul(f[1]); */
    }

    console.log(myifs);

    $: {
        // Generate a regular polygon with the given number of vertices
        // and half ratio for each vertex
        const pageCenter = [renderWidth / 2, renderHeight / 2];
        const radius = Math.min(renderWidth, renderHeight) * 0.4;

        points.set(
            Array.from({length: count}).map(
                (_, index) => index * 2 * Math.PI / count - Math.PI / 2
            ).map(angle => Matrix.columnVector([
                pageCenter[0] + Math.cos(angle) * radius,
                pageCenter[1] + Math.sin(angle) * radius,
                1
            ]))
        );

        ratios.set(
            Array.from({length: count}).fill(0.5)
        );
    }
</script>

<style>
    .app
    {
        display: flex;
        width: 100%;
        height: 100%;
    }

    .controls
    {
        flex: 1;
    }

    .render
    {
        flex: 1;
        position: relative;
    }

    .render .overlay
    {
        position: absolute;
        top: 0;
        left: 0;
    }
</style>

<div class="app" bind:clientWidth={pageWidth} bind:clientHeight={pageHeight}>
    <aside class="controls" style="width: {controlsWidth}px">
        <p>
            <button on:click={() => ++count}>Add point</button>
            <button on:click={() => --count}>Remove point</button>
        </p>

        <p>
            {#if controls}
            Controls visible.
            <button on:click={() => controls = false}>Hide</button>
            {:else}
            Controls hidden.
            <button on:click={() => controls = true}>Show</button>
            {/if}
        </p>
    </aside>

    <main class="render" style="width: {renderWidth}px">
        <Chaos
            width={renderWidth} height={renderHeight}
            start={$center} ifs={$ifs}
        />

        {#if controls}
            <svg
                class="overlay"
                width={renderWidth} height={renderHeight}
                viewBox="0 0 {renderWidth} {renderHeight}"
            >
                {#each $points as point}
                    <line
                        stroke="#333"
                        x1={$center.get(0, 0)}
                        y1={$center.get(1, 0)}
                        x2={point.get(0, 0)}
                        y2={point.get(1, 0)}
                    />
                {/each}
            </svg>

            {#each $points as point, index}
                <Point
                    x={point.get(0, 0)}
                    y={point.get(1, 0)}
                    on:move={evt => points.set([
                        ...$points.slice(0, index),
                        Matrix.columnVector([evt.detail.x, evt.detail.y, 1]),
                        ...$points.slice(index + 1)
                    ])}
                />
            {/each}

            {#each $ifs as current, index}
                <Point
                    x={current[1].mmul($center).get(0, 0)}
                    y={current[1].mmul($center).get(1, 0)}
                    on:move={evt => {
                        const point = Matrix.columnVector([evt.detail.x, evt.detail.y, 1]);

                        const vec1 = Matrix.sub($points[index], $center);
                        const vec2 = Matrix.sub(point, $center);

                        const nextRatio = 1 - vec1.dot(vec2) / vec1.dot(vec1);

                        if (evt.detail.shift)
                        {
                            ratios.set(
                                Array.from({length: $ratios.length}).fill(nextRatio)
                            );
                        }
                        else
                        {
                            ratios.set([
                                ...$ratios.slice(0, index),
                                nextRatio,
                                ...$ratios.slice(index + 1)
                            ]);
                        }
                    }}
                />
            {/each}
        {/if}
    </main>
</div>
