// bibiman - a TUI for managing BibLaTeX databases
// Copyright (C) 2024  lukeflo
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
/////

use crate::bibiman::bibisetup::BibiData;
use ratatui::widgets::{ScrollbarState, TableState};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EntryTableColumn {
    Authors,
    Title,
    Year,
    Pubtype,
}

// Define list containing entries as table
#[derive(Debug, PartialEq, Eq)]
pub struct EntryTable {
    pub entry_table_items: Vec<BibiData>,
    pub entry_table_at_search_start: Vec<BibiData>,
    pub entry_table_selected_column: EntryTableColumn,
    pub entry_table_sorted_by_col: Option<EntryTableColumn>,
    pub entry_table_reversed_sort: bool,
    pub entry_table_state: TableState,
    pub entry_scroll_state: ScrollbarState,
    pub entry_info_scroll: u16,
    pub entry_info_scroll_state: ScrollbarState,
    pub search_string: String, // pub entry_table_search: BibiSearch,
}

impl EntryTable {
    pub fn new(entry_list: Vec<BibiData>) -> Self {
        let mut entry_table_items = entry_list;
        entry_table_items.sort_by(|a, b| a.authors.to_lowercase().cmp(&b.authors.to_lowercase()));
        // entry_table
        let entry_table_state = TableState::default()
            .with_selected(0)
            .with_selected_column(1)
            // other two values above are ignored, if selected cell isn't fitting
            .with_selected_cell(Some((0, 1)));
        let entry_scroll_state = ScrollbarState::new(entry_table_items.len());
        let entry_info_scroll_state = ScrollbarState::default();
        Self {
            entry_table_items,
            entry_table_at_search_start: Vec::new(),
            entry_table_selected_column: EntryTableColumn::Authors,
            entry_table_sorted_by_col: Some(EntryTableColumn::Authors),
            entry_table_reversed_sort: false,
            entry_table_state,
            entry_scroll_state,
            entry_info_scroll: 0,
            entry_info_scroll_state,
            search_string: String::new(), // entry_table_search: BibiSearch::default(),
        }
    }

    pub fn sort_by_id(&mut self) {
        if self.entry_table_sorted_by_col.is_some() {
            self.entry_table_reversed_sort = false
        } else {
            self.entry_table_reversed_sort = !self.entry_table_reversed_sort;
        }
        if self.entry_table_reversed_sort {
            self.entry_table_items.sort_by(|a, b| b.id.cmp(&a.id));
        } else {
            self.entry_table_items.sort_by(|a, b| a.id.cmp(&b.id));
        }
        self.entry_table_sorted_by_col = None;
    }

    // Sort entry table by specific column.
    // Toggle sorting by hitting same key again
    pub fn sort_entry_table(&mut self, toggle: bool) {
        // Check if table was sorted by visible column before
        if toggle && self.entry_table_sorted_by_col.is_some() {
            self.entry_table_reversed_sort = !self.entry_table_reversed_sort;
        } else {
            self.entry_table_reversed_sort = false;
        }
        if let Some(sort_by_col) = &self.entry_table_sorted_by_col {
            if &self.entry_table_selected_column != sort_by_col {
                self.entry_table_reversed_sort = false
            }
        }
        self.entry_table_sorted_by_col = Some(self.entry_table_selected_column.clone());
        if self.entry_table_reversed_sort {
            match self.entry_table_selected_column {
                EntryTableColumn::Authors => self
                    .entry_table_items
                    .sort_by(|a, b| b.authors.to_lowercase().cmp(&a.authors.to_lowercase())),
                EntryTableColumn::Title => self
                    .entry_table_items
                    .sort_by(|a, b| b.title.to_lowercase().cmp(&a.title.to_lowercase())),
                EntryTableColumn::Year => self
                    .entry_table_items
                    .sort_by(|a, b| b.year.to_lowercase().cmp(&a.year.to_lowercase())),
                EntryTableColumn::Pubtype => self.entry_table_items.sort_by(|a, b| {
                    b.custom_field_value()
                        .to_lowercase()
                        .cmp(&a.custom_field_value().to_lowercase())
                }),
            }
        } else if !self.entry_table_reversed_sort {
            match self.entry_table_selected_column {
                EntryTableColumn::Authors => self
                    .entry_table_items
                    .sort_by(|a, b| a.authors.to_lowercase().cmp(&b.authors.to_lowercase())),
                EntryTableColumn::Title => self
                    .entry_table_items
                    .sort_by(|a, b| a.title.to_lowercase().cmp(&b.title.to_lowercase())),
                EntryTableColumn::Year => self
                    .entry_table_items
                    .sort_by(|a, b| a.year.to_lowercase().cmp(&b.year.to_lowercase())),
                EntryTableColumn::Pubtype => self.entry_table_items.sort_by(|a, b| {
                    a.custom_field_value()
                        .to_lowercase()
                        .cmp(&b.custom_field_value().to_lowercase())
                }),
            }
        }
    }

    /// Try to get currently selected entry. If not possible return an error message
    pub(crate) fn get_selected_entry(&mut self) -> Result<&BibiData, String> {
        if let Some(idx) = self.entry_table_state.selected() {
            if let Some(entry) = self.entry_table_items.get(idx) {
                Ok(entry)
            } else {
                Err(String::from("No entry found for given index"))
            }
        } else {
            Err(String::from(
                "Couldn't get entry index. Propably no entry selected",
            ))
        }
    }

    pub(crate) fn search_string(&self) -> &str {
        &self.search_string
    }

    pub(crate) fn set_search_string<S: ToString>(&mut self, value: S) {
        self.search_string = value.to_string();
    }
}
