#pragma once
#include <vector>
#include <string>
#include <optional>
#include <iostream>
#include <thread>
#include <mutex>
#include <map>
#include <unordered_map>
#include <deque>
#include <regex>
#include <qstandarditemmodel.h>
#include <qdatetime.h>
#include <mupdf/fitz.h>
#include <qobject.h>
#include <qnetworkreply.h>
#include <qjsondocument.h>
#include <qurlquery.h>
#include "book.h"
#include "coordinates.h"
class CachedChecksummer;
class DatabaseManager;
class CharacterIterator {
fz_stext_block* block = nullptr;
fz_stext_line* line = nullptr;
fz_stext_char* chr = nullptr;
public:
CharacterIterator(fz_stext_page* page);
CharacterIterator(fz_stext_block* b, fz_stext_line* l, fz_stext_char* c);
CharacterIterator& operator++();
CharacterIterator operator++(int);
bool operator==(const CharacterIterator& other) const;
bool operator!=(const CharacterIterator& other) const;
std::tuple<fz_stext_block*, fz_stext_line*, fz_stext_char*> operator*() const;
using difference_type = long;
using value_type = std::tuple<fz_stext_block*, fz_stext_line*, fz_stext_char*>;
using pointer = const std::tuple<fz_stext_block*, fz_stext_line*, fz_stext_char*>*;
using reference = const std::tuple<fz_stext_block*, fz_stext_line*, fz_stext_char*>&;
using iterator_category = std::forward_iterator_tag;
};
class PageIterator {
fz_stext_page* page;
public:
PageIterator(fz_stext_page* page);
CharacterIterator begin() const;
CharacterIterator end() const;
};
struct CachedPageIndex {
std::wstring text;
std::vector<PagelessDocumentRect> rects;
};
class Document {
private:
std::mutex drawings_mutex;
std::map<int, std::vector<FreehandDrawing>> page_freehand_drawings;
bool is_drawings_dirty = false;
bool is_annotations_dirty = false;
std::vector<Mark> marks;
std::vector<BookMark> bookmarks;
std::vector<Highlight> highlights;
std::vector<Portal> portals;
DatabaseManager* db_manager = nullptr;
std::vector<TocNode*> top_level_toc_nodes;
std::vector<TocNode*> created_top_level_toc_nodes;
std::vector<std::wstring> flat_toc_names;
std::vector<int> flat_toc_pages;
std::map<int, std::vector<AbsoluteRect>> cached_page_line_rects;
std::map<int, std::vector<std::wstring>> cached_line_texts;
std::deque<std::pair<int, CachedPageIndex>> cached_page_index;
bool super_fast_search_index_ready = false;
std::wstring super_fast_search_index;
std::vector<int> super_fast_page_begin_indices;
QJsonObject extras;
int page_offset = 0;
std::optional<int> cached_num_pages = {};
std::vector<std::pair<int, fz_stext_page*>> cached_stext_pages;
std::vector<std::pair<int, fz_pixmap*>> cached_small_pixmaps;
std::map<int, std::optional<std::string>> cached_fastread_highlights;
PdfLink merge_links(const std::vector<PdfLink>& links_to_merge);
fz_context* context = nullptr;
std::wstring file_name;
std::unordered_map<int, fz_link*> cached_page_links;
std::unordered_map<int, std::vector<PdfLink>> cached_merged_pdf_links;
std::unordered_map<int, std::vector<PagelessDocumentRect>> cached_flat_words;
std::unordered_map<int, std::vector<std::vector<PagelessDocumentRect>>> cached_flat_word_chars;
QStandardItemModel* cached_toc_model = nullptr;
std::vector<float> accum_page_heights;
std::vector<float> page_heights;
std::vector<float> page_widths;
std::vector<std::wstring> page_labels;
std::mutex page_dims_mutex;
std::string correct_password = "";
bool password_was_correct = false;
bool document_needs_password = false;
std::vector<IndexedData> generic_indices;
std::map<std::wstring, IndexedData> reference_indices;
std::map<std::wstring, std::vector<IndexedData>> equation_indices;
std::mutex document_indexing_mutex;
std::optional<std::thread> document_indexing_thread = {};
bool is_document_indexing_required = true;
bool is_indexing = false;
bool are_highlights_loaded = false;
bool should_render_annotations = true;
bool should_reload_annotations = false;
QDateTime last_update_time;
CachedChecksummer* checksummer;
bool* invalid_flag_pointer = nullptr;
int get_mark_index(char symbol);
fz_outline* get_toc_outline();
void create_toc_tree(std::vector<TocNode*>& toc);
Document(fz_context* context, std::wstring file_name, DatabaseManager* db_manager, CachedChecksummer* checksummer);
void clear_toc_nodes();
void clear_toc_node(TocNode* node);
int find_highlight_index_with_uuid(const std::string& uuid);
public:
fz_document* doc = nullptr;
std::wstring detected_paper_name = L"";
PageIterator page_iterator(int page_number);
int get_page_text_and_line_rects_after_rect(int page_number,
AbsoluteRect after,
std::wstring& text,
std::vector<PagelessDocumentRect>& line_rects,
std::vector<PagelessDocumentRect>& char_rects);
void load_document_metadata_from_db();
std::string add_bookmark(const std::wstring& desc, float y_offset);
std::string add_marked_bookmark(const std::wstring& desc, AbsoluteDocumentPos pos);
int add_incomplete_bookmark(BookMark incomplete_bookmark);
std::string add_pending_bookmark(int index, const std::wstring& desc);
void undo_pending_bookmark(int index);
void add_freetext_bookmark(const std::wstring& desc, AbsoluteRect absrect);
void add_freetext_bookmark_with_color(const std::wstring& desc, AbsoluteRect absrect, float* color, float font_size = -1);
std::string add_highlight(const std::wstring& desc, const std::vector<AbsoluteRect>& highlight_rects, AbsoluteDocumentPos selection_begin, AbsoluteDocumentPos selection_end, char type);
std::string add_highlight(const std::wstring& annot, AbsoluteDocumentPos selection_begin, AbsoluteDocumentPos selection_end, char type);
void delete_highlight_with_index(int index);
void delete_bookmark_with_index(int index);
void delete_highlight(Highlight hl);
int get_bookmark_index_at_pos(AbsoluteDocumentPos abspos);
int get_portal_index_at_pos(AbsoluteDocumentPos abspos);
bool should_render_pdf_annotations();
void set_should_render_pdf_annotations(bool val);
bool get_should_render_pdf_annotations();
std::vector<Portal> get_intersecting_visible_portals(float absrange_begin, float absrange_end);
CachedPageIndex& get_page_index(int page);
void fill_search_result(SearchResult* result);
QString get_rest_of_document_pages_text(int from);
int get_page_offset_into_super_fast_index(int from);
int get_page_from_character_offset(int offset);
void fill_highlight_rects(fz_context* ctx, fz_document* doc);
void fill_index_highlight_rects(int highlight_index, fz_context* thread_context = nullptr, fz_document* thread_document = nullptr);
void count_chapter_pages(std::vector<int>& page_counts);
void convert_toc_tree(fz_outline* root, std::vector<TocNode*>& output);
void count_chapter_pages_accum(std::vector<int>& page_counts);
bool get_is_indexing();
fz_stext_page* get_stext_with_page_number(fz_context* ctx, int page_number, fz_document* doc = nullptr);
fz_stext_page* get_stext_with_page_number(int page_number);
int add_portal(Portal link, bool insert_into_database = true);
std::wstring get_path();
std::string get_checksum();
std::optional<std::string> get_checksum_fast();
int find_closest_bookmark_index(const std::vector<BookMark>& sorted_bookmarks, float to_offset_y) const;
int find_closest_portal_index(const std::vector<Portal>& sorted_bookmarks, float to_offset_y) const;
int find_closest_highlight_index(const std::vector<Highlight>& sorted_highlights, float to_offset_y) const;
std::optional<Portal> find_closest_portal(float to_offset_y, int* index = nullptr);
bool update_portal(Portal new_link);
void delete_closest_bookmark(float to_y_offset);
void delete_bookmark(int index);
void delete_closest_portal(float to_offset_y);
int get_portal_index_with_uuid(const std::string& uuid);
void delete_portal_with_uuid(const std::string& uuid);
std::vector<BookMark>& get_bookmarks();
std::vector<Portal>& get_portals();
std::vector<BookMark> get_sorted_bookmarks() const;
std::vector<Portal> get_sorted_portals() const;
const std::vector<Highlight>& get_highlights() const;
int get_highlight_index_with_uuid(std::string uuid);
int get_bookmark_index_with_uuid(std::string uuid);
const std::vector<Highlight> get_highlights_of_type(char type) const;
const std::vector<Highlight> get_highlights_sorted(char type = 0) const;
std::optional<Highlight> get_next_highlight(float abs_y, char type = 0, int offset = 0) const;
std::optional<Highlight> get_prev_highlight(float abs_y, char type = 0, int offset = 0) const;
fz_link* get_page_links(int page_number);
const std::vector<PdfLink>& get_page_merged_pdf_links(int page_number);
PdfLink pdf_link_from_fz_link(int page, fz_link* link);
void add_mark(char symbol, float y_offset, std::optional<float> x_offset, std::optional<float> zoom_level);
bool remove_mark(char symbol);
std::optional<Mark> get_mark_if_exists(char symbol);
~Document();
const std::vector<TocNode*>& get_toc();
bool has_toc();
const std::vector<std::wstring>& get_flat_toc_names();
const std::vector<int>& get_flat_toc_pages();
bool open(bool* invalid_flag, bool force_load_dimensions = false, std::string password = "", bool temp = false);
void reload(std::string password = "");
QDateTime get_last_edit_time();
unsigned int get_milies_since_last_document_update_time();
unsigned int get_milies_since_last_edit_time();
float get_page_height(int page_index);
fz_pixmap* get_small_pixmap(int page);
float get_page_width(int page_index);
float get_page_width_median();
std::wstring get_page_label(int page_index);
int get_page_number_with_label(std::wstring page_label);
bool is_reflowable();
float get_page_size_smart(bool width, int page_index, float* left_ratio, float* right_ratio, int* normal_page_width);
float get_accum_page_height(int page_index);
void rotate();
void get_visible_pages(float doc_y_range_begin, float doc_y_range_end, std::vector<int>& visible_pages);
void load_page_dimensions(bool force_load_now);
int num_pages();
AbsoluteRect get_page_absolute_rect(int page);
DocumentPos absolute_to_page_pos(AbsoluteDocumentPos absolute_pos);
DocumentPos absolute_to_page_pos_uncentered(AbsoluteDocumentPos absolute_pos);
DocumentRect absolute_to_page_rect(AbsoluteRect abs_rect);
QStandardItemModel* get_toc_model();
int get_offset_page_number(float y_offset);
void index_document(bool* invalid_flag);
void stop_indexing();
void delete_page_intersecting_drawings(int page, AbsoluteRect absolute_rect, bool mask[26]);
void delete_all_page_drawings(int page);
void delete_all_drawings();
std::vector<SelectedObjectIndex> get_page_intersecting_drawing_indices(int page, AbsoluteRect absolute_rect, bool mask[26]);
std::vector<IndexedData> find_reference_with_string(std::wstring reference_name, int page_number);
std::vector<IndexedData> find_equation_with_string(std::wstring equation_name, int page_number);
std::vector<IndexedData> find_generic_with_string(std::wstring equation_name, int page_number);
std::optional<std::wstring> get_text_at_position(const std::vector<fz_stext_char*>& flat_chars, PagelessDocumentPos position);
std::optional<std::wstring> get_reference_text_at_position(const std::vector<fz_stext_char*>& flat_chars, PagelessDocumentPos position, std::pair<int, int>* out_range);
std::optional<std::wstring> get_paper_name_at_position(const std::vector<fz_stext_char*>& flat_chars, PagelessDocumentPos position);
std::optional<std::wstring> get_equation_text_at_position(const std::vector<fz_stext_char*>& flat_chars, PagelessDocumentPos position, std::pair<int, int>* out_range);
std::optional<std::pair<std::wstring, std::wstring>> get_generic_link_name_at_position(const std::vector<fz_stext_char*>& flat_chars, PagelessDocumentPos position, std::pair<int, int>* out_range);
std::optional<std::wstring> get_regex_match_at_position(const std::wregex& regex, const std::vector<fz_stext_char*>& flat_chars, PagelessDocumentPos position, std::pair<int, int>* out_range);
std::optional<std::wstring> get_text_at_position(DocumentPos position);
std::optional<std::wstring> get_reference_text_at_position(DocumentPos position, std::pair<int, int>* out_range);
std::optional<std::wstring> get_paper_name_at_position(DocumentPos position);
std::optional<std::wstring> get_equation_text_at_position(DocumentPos position, std::pair<int, int>* out_range);
std::optional<std::pair<std::wstring, std::wstring>> get_generic_link_name_at_position(DocumentPos position, std::pair<int, int>* out_range);
std::optional<std::wstring> get_regex_match_at_position(const std::wregex& regex, DocumentPos position, std::pair<int, int>* out_range);
std::vector<DocumentPos> find_generic_locations(const std::wstring& type, const std::wstring& name);
bool can_use_highlights();
bool load_extras();
bool persist_extras();
std::optional<QVariant> get_extra(QString name);
template<typename T>
void set_extra(QString name, T value) {
extras[name] = value;
}
std::vector<std::wstring> get_page_bib_candidates(int page_number, std::vector<PagelessDocumentRect>* out_end_rects = nullptr);
std::optional<std::pair<std::wstring, PagelessDocumentRect>> get_page_bib_with_reference(int page_number, std::wstring reference_text);
void get_line_selection(AbsoluteDocumentPos selection_begin,
AbsoluteDocumentPos selection_end,
std::deque<AbsoluteRect>& selected_characters,
std::wstring& selected_text);
void get_text_selection(AbsoluteDocumentPos selection_begin,
AbsoluteDocumentPos selection_end,
bool is_word_selection,
std::deque<AbsoluteRect>& selected_characters,
std::wstring& selected_text);
void get_text_selection(fz_context* ctx, AbsoluteDocumentPos selection_begin,
AbsoluteDocumentPos selection_end,
bool is_word_selection,
std::deque<AbsoluteRect>& selected_characters,
std::wstring& selected_text,
fz_document* doc = nullptr);
std::wstring get_raw_text_selection(AbsoluteDocumentPos selection_begin, AbsoluteDocumentPos selection_end);
bool is_bookmark_new(const BookMark& bookmark);
bool is_highlight_new(const Highlight& highlight);
bool is_drawing_new(const FreehandDrawing& drawing);
int num_freehand_drawings();
std::vector<BookMark> get_new_sioyek_bookmarks(const std::vector<BookMark>& pdf_bookmarks);
std::vector<Highlight> get_new_sioyek_highlights(const std::vector<Highlight>& pdf_highlights);
fz_context* get_mupdf_context();
int get_page_offset();
void set_page_offset(int new_offset);
void embed_annotations(std::wstring new_file_path);
void embed_generated_toc(std::wstring new_file_path);
void export_generated_toc(std::wstring new_file_path);
void get_pdf_annotations(std::vector<BookMark>& pdf_bookmarks, std::vector<Highlight>& pdf_highlights, std::vector<FreehandDrawing>& pdf_drawings);
void import_annotations();
std::vector<PagelessDocumentRect> get_page_flat_words(int page);
std::vector<std::vector<PagelessDocumentRect>> get_page_flat_word_chars(int page);
void clear_document_caches();
void load_document_caches(bool* invalid_flag, bool force_now);
int reflow(int page);
void update_highlight_add_text_annotation(const std::string& uuid, const std::wstring& text_annot);
void update_highlight_type(const std::string& uuid, char new_type);
void update_highlight_type(int index, char new_type);
void update_bookmark_text(int index, const std::wstring& new_text, float new_font_size);
void update_bookmark_position(int index, AbsoluteDocumentPos new_begin_position, AbsoluteDocumentPos new_end_position);
void update_portal_src_position(int index, AbsoluteDocumentPos new_position);
bool needs_password();
bool needs_authentication();
bool apply_password(const char* password);
std::vector<PagelessDocumentRect> get_highlighted_character_masks(int page);
PagelessDocumentRect get_page_rect_no_cache(int page);
std::optional<PdfLink> get_link_in_pos(int page, float x, float y);
std::optional<PdfLink> get_link_in_pos(const DocumentPos& pos);
std::vector<PdfLink> get_links_in_page_rect(int page, AbsoluteRect rect);
std::wstring get_pdf_link_text(PdfLink link);
std::string get_highlight_index_uuid(int index);
std::string get_bookmark_index_uuid(int index);
int add_stext_page_to_created_toc(fz_stext_page* stext_page,
int page_number,
std::vector<TocNode*>& toc_node_stack,
std::vector<TocNode*>& top_level_node);
float document_to_absolute_y(int page, float doc_y);
AbsoluteDocumentPos document_to_absolute_pos(DocumentPos docpos);
AbsoluteRect document_to_absolute_rect(DocumentRect doc_rect);
AbsoluteRect get_ith_next_line_from_absolute_y(int page, int line_index, int i, bool continue_to_next_page, int* out_index, int* out_page);
const std::vector<AbsoluteRect>& get_page_lines(
int page,
std::vector<std::wstring>* line_texts = nullptr,
std::vector<std::vector<PagelessDocumentRect>>* out_line_rects = nullptr);
std::wstring get_addtional_sioyek_file_path(QString type);
std::wstring get_drawings_file_path();
std::wstring get_scratchpad_file_path();
std::wstring get_extras_file_path();
std::wstring get_annotations_file_path();
bool annotations_file_exists();
bool annotations_file_is_newer_than_database();
std::optional<AbsoluteRect> get_rect_vertically(bool below, AbsoluteRect rect);
void persist_drawings(bool force = false);
void persist_annotations(bool force = false);
void load_drawings();
void load_annotations(bool sync = false);
void load_drawings_async();
std::wstring detect_paper_name(fz_context* context, fz_document* doc);
std::wstring detect_paper_name();
bool is_super_fast_index_ready();
std::vector<SearchResult> search_text(std::wstring query, SearchCaseSensitivity case_sensitive, int begin_page, int min_page, int max_page);
std::vector<SearchResult> search_regex(std::wstring query, SearchCaseSensitivity case_sensitive, int begin_page, int min_page, int max_page);
float max_y_offset();
void add_freehand_drawing(FreehandDrawing new_drawing);
void get_page_freehand_drawings_with_indices(int page, const std::vector<SelectedObjectIndex>& indices, std::vector<FreehandDrawing>& freehand_drawings, std::vector<PixmapDrawing>& pixmap_drawings);
void undo_freehand_drawing();
const std::vector<FreehandDrawing>& get_page_drawings(int page);
AbsoluteRect to_absolute(int page, fz_quad quad);
AbsoluteRect to_absolute(int page, PagelessDocumentRect rect);
bool get_should_reload_annotations();
void reload_annotations_on_new_checksum();
int find_reference_page_with_reference_text(std::wstring query);
std::optional<DocumentPos> find_abbreviation(std::wstring abbr, std::vector<DocumentRect>& overview_highlight_rects);
QJsonArray get_bookmarks_json();
QJsonArray get_highlights_json();
QJsonArray get_portals_json();
QJsonArray get_marks_json();
std::wstring get_detected_paper_name_if_exists();
std::wstring& get_super_fast_search_index();
std::vector<int>& get_super_fast_search_page_indices();
fz_stext_char* get_next_char_after_selection(int page_number, fz_point document_point);
friend class DocumentManager;
};
class DocumentManager {
private:
fz_context* mupdf_context = nullptr;
DatabaseManager* db_manager = nullptr;
CachedChecksummer* checksummer;
std::unordered_map<std::wstring, Document*> cached_documents;
std::unordered_map<std::string, std::wstring> hash_to_path;
std::vector<std::wstring> tabs;
public:
DocumentManager(fz_context* mupdf_context, DatabaseManager* db_manager, CachedChecksummer* checksummer);
int get_tab_index(const std::wstring& path);
int add_tab(const std::wstring& path);
void remove_tab(const std::wstring& path);
std::vector<std::wstring> get_tabs();
Document* get_document(const std::wstring& path);
std::optional<std::wstring> get_path_from_hash(const std::string& checksum);
Document* get_document_with_checksum(const std::string& checksum);
std::optional<Document*> get_cached_document(const std::wstring& path);
void free_document(Document* document);
const std::unordered_map<std::wstring, Document*>& get_cached_documents();
std::vector<std::wstring> get_loaded_document_paths();
void delete_global_mark(char symbol);
~DocumentManager();
};