// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

use perspective_client::config::*;
use perspective_js::utils::ApiFuture;
use web_sys::*;
use yew::prelude::*;

use crate::components::containers::dragdrop_list::*;
use crate::components::type_icon::TypeIcon;
use crate::presentation::Presentation;
use crate::renderer::*;
use crate::session::*;
use crate::utils::*;

#[derive(Properties)]
pub struct SortColumnProps {
    pub sort: Sort,
    pub idx: usize,

    /// Session metadata snapshot — threaded from `SessionProps`.
    pub metadata: SessionMetadataRc,

    /// Current view config — threaded as a value prop.
    pub view_config: PtrEqRc<ViewConfig>,

    // State
    pub session: Session,
    pub renderer: Renderer,
    pub presentation: Presentation,
}

impl PartialEq for SortColumnProps {
    fn eq(&self, other: &Self) -> bool {
        self.sort == other.sort
            && self.idx == other.idx
            && self.metadata == other.metadata
            && self.view_config == other.view_config
    }
}

impl DragDropListItemProps for SortColumnProps {
    type Item = Sort;

    fn get_item(&self) -> Sort {
        self.sort.clone()
    }
}

pub enum SortColumnMsg {
    SortDirClick(bool),
}

/// A `SortColumn` includes the column name and `SortDir` arrow, a clickable
/// button which cycles through the available `SortDir` states.
pub struct SortColumn {}

impl Component for SortColumn {
    type Message = SortColumnMsg;
    type Properties = SortColumnProps;

    fn create(_ctx: &Context<Self>) -> Self {
        Self {}
    }

    fn update(&mut self, ctx: &Context<Self>, msg: SortColumnMsg) -> bool {
        match msg {
            SortColumnMsg::SortDirClick(shift_key) => {
                let is_split = ctx.props().view_config.split_by.is_empty();
                let mut sort = ctx.props().view_config.sort.clone();
                let sort_column = &mut sort.get_mut(ctx.props().idx).expect("Sort on no column");
                sort_column.1 = sort_column.1.cycle(!is_split, shift_key);
                let update = ViewConfigUpdate {
                    sort: Some(sort),
                    ..ViewConfigUpdate::default()
                };

                let session = ctx.props().session.clone();
                let renderer = ctx.props().renderer.clone();
                if session.update_view_config(update).is_ok() {
                    ApiFuture::spawn(async move {
                        renderer.apply_pending_plugin()?;
                        renderer.draw(session.validate().await?.create_view()).await
                    });
                }

                false
            },
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let onclick = ctx
            .link()
            .callback(|event: MouseEvent| SortColumnMsg::SortDirClick(event.shift_key()));

        let dragstart = Callback::from({
            let event_name = ctx.props().sort.0.to_owned();
            let presentation = ctx.props().presentation.clone();
            move |event: DragEvent| {
                presentation.set_drag_image(&event).unwrap();
                presentation
                    .notify_drag_start(event_name.to_string(), DragEffect::Move(DragTarget::Sort))
            }
        });

        let dragend = Callback::from({
            let presentation = ctx.props().presentation.clone();
            move |_event| presentation.notify_drag_end()
        });

        let col_type = ctx
            .props()
            .metadata
            .get_column_table_type(&ctx.props().sort.0.to_owned())
            .unwrap_or(ColumnType::Integer);

        html! {
            <div
                class="pivot-column-draggable"
                draggable="true"
                ondragstart={dragstart}
                ondragend={dragend}
            >
                <div class="pivot-column-border">
                    <span class="drag-handle icon" />
                    <TypeIcon ty={col_type} />
                    // <TypeIcon ty={ColumnType::String} />
                    <span class="column_name">{ ctx.props().sort.0.to_owned() }</span>
                    <span
                        class={format!("sort-icon {}", ctx.props().sort.1)}
                        onmousedown={onclick}
                    />
                </div>
            </div>
        }
    }
}
