<script setup lang="ts">
import { computed, provide, ref } from 'vue';
import { useNow } from '@/composables/ticker';
import { GRAPH_DATA, type GraphData } from '@/graph/graph';

const props = defineProps<{
    width: number;
    height: number;
    border_left_right?: number;
    border_top_bottom?: number;
    utc?: boolean;
}>();

const width = computed(() => {
    return props.width;
});

const height = computed(() => {
    return props.height;
});

const now = useNow(33);
const window_duration = 10 * 1000; // 10 seconds

const time_lines = [
    1, // 1ms
    2, // 2ms
    5, // 5ms
    10, // 10ms
    20, // 20ms
    50, // 50ms
    100, // 100ms
    200, // 200ms
    500, // 500ms
    1000, // 1s
    2000, // 2s
    5000, // 5s
    10000, // 10s
    30000, // 30s
    60000, // 1m
    300000, // 5m
    6000000, // 10m
    18000000, // 30m
    36000000, // 1h
    72000000, // 2h
    144000000, // 4h
    216000000, // 6h
    432000000, // 12h
    864000000, // 1d
    1728000000, // 2d
    6048000000, // 1w
];
time_lines.reverse();
const text_offset = computed(() => 5);

const border_left_right = computed(() => props.border_left_right || 0);
const border_top_bottom = computed(() => props.border_top_bottom || 0);

const max_x = now;
const min_x = computed(() => max_x.value - window_duration);

const x_map = (x: number) => {
    const diff_x = max_x.value - min_x.value;
    return (
        ((width.value - 2 * border_left_right.value) * (x - min_x.value)) /
            diff_x +
        border_left_right.value
    );
};

const telemetry_lines = ref([]);

provide<GraphData>(GRAPH_DATA, {
    border_top_bottom: border_top_bottom,
    min_x: min_x,
    max_x: now,
    width: () => width.value - 2 * border_left_right.value,
    height: () => height.value - 2 * border_top_bottom.value,
    x_map: x_map,
    lines: telemetry_lines,
});

const duration = computed(() => {
    const diff_x = max_x.value - min_x.value;
    return time_lines.find((duration) => diff_x / duration >= 3)!;
});

const lines = computed(() => {
    const result = [];
    for (
        let i = Math.ceil(max_x.value / duration.value);
        i >= Math.ceil(min_x.value / duration.value) - 5;
        i--
    ) {
        const x = i * duration.value;
        result.push(x);
    }
    return result;
});

function getDateString(date: Date) {
    const year = props.utc ? date.getUTCFullYear() : date.getFullYear();
    const month = (
        (props.utc ? date.getMonth() : date.getMonth()) + 1
    ).toLocaleString('en-US', {
        minimumIntegerDigits: 2,
        useGrouping: false,
        maximumFractionDigits: 0,
    });
    const day = (props.utc ? date.getUTCDate() : date.getDate()).toLocaleString(
        'en-US',
        {
            minimumIntegerDigits: 2,
            useGrouping: false,
            maximumFractionDigits: 0,
        },
    );
    const hour = (
        props.utc ? date.getUTCHours() : date.getHours()
    ).toLocaleString('en-US', {
        minimumIntegerDigits: 2,
        useGrouping: false,
        maximumFractionDigits: 0,
    });
    const minute = (
        props.utc ? date.getUTCMinutes() : date.getMinutes()
    ).toLocaleString('en-US', {
        minimumIntegerDigits: 2,
        useGrouping: false,
        maximumFractionDigits: 0,
    });
    const second = (
        props.utc ? date.getUTCSeconds() : date.getSeconds()
    ).toLocaleString('en-US', {
        minimumIntegerDigits: 2,
        useGrouping: false,
        maximumFractionDigits: 0,
    });
    const milliseconds = (
        props.utc ? date.getUTCMilliseconds() : date.getMilliseconds()
    ).toLocaleString('en-US', {
        minimumIntegerDigits: 3,
        useGrouping: false,
        maximumFractionDigits: 0,
    });
    return `${year}/${month}/${day} ${hour}:${minute}:${second}${duration.value < 1000 ? `.${milliseconds}` : ''}${props.utc ? 'Z' : ''}`;
}
</script>

<template>
    <svg ref="svg_graph" class="graph" :width="width" :height="height">
        <defs>
            <clipPath id="content">
                <rect
                    :x="border_left_right"
                    :y="border_top_bottom"
                    :width="width - border_left_right * 2"
                    :height="height - border_top_bottom * 2"
                ></rect>
            </clipPath>
            <clipPath id="y_ticker">
                <rect
                    :x="0"
                    :y="border_top_bottom"
                    :width="width"
                    :height="height - border_top_bottom * 2"
                ></rect>
            </clipPath>
            <clipPath id="x_ticker">
                <rect
                    :x="border_left_right"
                    :y="0"
                    :width="width - border_left_right * 2"
                    :height="height"
                ></rect>
            </clipPath>
        </defs>
        <g class="time_tick" clip-path="url(#x_ticker)">
            <template v-for="tick of lines" :key="tick">
                <polyline
                    :points="`${x_map(tick)},${border_top_bottom} ${x_map(tick)},${height - border_top_bottom}`"
                ></polyline>
                <text
                    class="bottom_edge"
                    :x="x_map(tick)"
                    :y="height - border_top_bottom + text_offset"
                >
                    {{ getDateString(new Date(tick)) }}
                </text>
            </template>
        </g>
        <slot></slot>
    </svg>
</template>

<style scoped lang="scss">
@use '@/assets/variables';

.bottom_edge {
    text-anchor: middle;
    alignment-baseline: text-before-edge;
    font-family: variables.$text-font;
}

.time_tick {
    stroke: variables.$time-tick;
    fill: variables.$time-tick;
}
</style>
