/*
ssc (static site checker)
Copyright (c) 2020 Dylan Harris
https://dylanharris.org/

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public Licence as published by
the Free Software Foundation, either version 3 of the Licence,  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 Licence for more details.

You should have received a copy of the GNU General Public
Licence along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "standard.h"
#include "element.h"
#include "page.h"
#include "attribute_classes.h"

void element::examine_tab ()
{   if (node_.version () == html_3_0)
        if (a_.known (a_indent) && a_.known (a_to))
            pick (nit_chocolate_teapot, ed_3, "Horizontal Tabs (Page 38)", es_warning, ec_element, "TO and INDENT should not be combined in <TAB>"); }

void element::examine_table ()
{   if (! has_child () || (node_.version ().major () < 5)) return;
    typedef enum { to_start, to_caption, to_colgroup, to_head, to_foot_start, to_tr, to_foot_end } table_order;
    table_order tor = to_start;
    bool ooo = false, footed = false, doubled = false, mixed = false, body = false, tr = false;
    for (element_ptr p = child_; p != nullptr; p = p -> sibling_)
        if (! p -> node_.is_closure ())
        {   switch (p -> tag ())
            {   case elem_caption :
                    doubled = (tor == to_caption);
                    ooo = (tor != to_start);
                    tor = to_caption;
                    break;
                case elem_colgroup :
                    ooo = (tor > to_colgroup);
                    tor = to_colgroup;
                    break;
                case elem_thead :
                    doubled = (tor == to_head);
                    ooo = (tor >= to_head);
                    tor = to_head;
                    break;
                case elem_tbody :
                    mixed = tr;
                    body = true;
                    ooo = (tor > to_tr);
                    tor = to_tr;
                    break;
                case elem_tr :
                    mixed = body;
                    tr = true;
                    ooo = (tor > to_tr);
                    tor = to_tr;
                    break;
                case elem_tfoot :
                    doubled = ooo = footed;
                    if ((w3_minor_5 (node_.version ()) == 0) && (tor < to_tr)) tor = to_foot_start;
                    else tor = to_foot_end;
                    footed = true;
                    break; }
        if (ooo)
            if (w3_minor_5 (node_.version ()) == 0)
                pick (nit_table_children, ed_50, "4.9.1 The table element", es_error, ec_element, "<TABLE> children in wrong order (<CAPTION>, <COLGROUP>s, <THEAD>, <TFOOT>, <TR>s or <TBODY>s, <TFOOT>, with only one <TFOOT>)");
            else pick (nit_table_children, ed_51, "4.9.1 The table element", es_error, ec_element, "<TABLE> children in wrong order (<CAPTION>, <COLGROUP>s, <THEAD>, <TR>s or <TBODY>s, <TFOOT>)");
        if (doubled) pick (nit_table_children, ed_50, "4.9.1 The table element", es_error, ec_element, "only one <CAPTION>, <THEAD>, <TFOOT> per <TABLE>");
        if (mixed) pick (nit_table_children, ed_50, "4.9.1 The table element", es_error, ec_element, "either <TBODY>s or <TR>s, not both");
        if (ooo || doubled || mixed) break; } }

void element::examine_td ()
{   if (node_.version ().major () < 5) return;
    span_check (); }

void element::examine_textarea ()
{   no_anchor_daddy ();
    if (a_.good (a_wrap))
        if (a_.get_int (a_wrap) == static_cast < int > (w_hard))
            if (! a_.known (a_cols))
                pick (nit_bad_textarea, ed_50, "4.10.11 The textarea element", es_error, ec_element, "When <TEXTAREA> WRAP='hard', COLS is required");
    int max = 0, min = 1;
    if (a_.good (a_maxlength)) max = a_.get_int (a_maxlength);
    if (a_.good (a_minlength)) min = a_.get_int (a_minlength);
    int len = static_cast < int > (text ().length ());
    if ((max > 0) && (min > max)) pick (nit_bad_textarea, ed_50, "4.10.11 The textarea element", es_error, ec_element, "<TEXTAREA> MINLENGTH is greater than MAXLENGTH");
    if (len > 0)
    {   if (len < min) pick (nit_bad_textarea, ed_50, "4.10.11 The textarea element", es_error, ec_element, "<TEXTAREA> content is short than MAXLENGTH");
        if ((max > 0) && (len > max)) pick (nit_bad_textarea, ed_50, "4.10.11 The textarea element", es_error, ec_element, "<TEXTAREA> content is longer than MAXLENGTH"); } }

void element::examine_th ()
{   if (node_.version ().major () < 5) return;
    bool is3 = (w3_minor_5 (node_.version ()) == 3);
    element_bitset bs (descendant_elements_);
    if (is3) bs &= sectioning_bitset | header_bitset | elem_header | elem_footer | elem_main;
    else bs &= sectioning_bitset | header_bitset | elem_header | elem_footer;
    if (bs.any ())
        if (is3) pick (nit_bad_descendant, ed_53, "4.9.10 The th element ", es_error, ec_element, "<TH> cannot have <MAIN>, <HEADER>, <FOOTER>, sectioning or header descendants");
        else pick (nit_bad_descendant, ed_50, "4.9.10 The th element ", es_error, ec_element, "<TH> cannot have <HEADER>, <FOOTER>, sectioning or header descendants");
    span_check (); }

void element::examine_time ()
{   if (! a_.known (a_datetime))
    {   element_bitset bs (descendant_elements_);
        bs &= ~non_standard_bitset;
        if (bs.any ())
            pick (nit_use_datetime, ed_50, "4.5.11 The time element", es_error, ec_element, "<TIME> without DATETIME cannot have descendant elements");
        type_master < t_datetime_5 > dt;
        dt.set_value (node_.nits (), node_.version (), text ());
        if (! dt.good ())
            pick (nit_use_datetime, es_warning, ec_element, "<TIME>'s descendant text is not particularly timely; perhaps use a DATETIME attribute"); } }

void element::examine_title ()
{   if (! node_.version ().has_svg ()) only_one_of (elem_title);
    ::std::string txt (text ());
    if (is_whitespace (txt))
        pick (nit_text_content, es_warning, ec_element, "<TITLE> text should be more than whitespace");
    else if (txt.length () > MAX_IDEAL_TITLE_LENGTH)
        pick (nit_long_title, ed_tags, "TITLE section", es_warning, ec_element, "the TITLE text (", quote (txt.substr (0, MAX_IDEAL_TITLE_LENGTH)), "...) should be fairly short");
    page_.confirm_title (); }

void element::examine_track ()
{   if (node_.version ().major () >= 5)
    {   e_kind k = static_cast < e_kind > (a_.get_int (a_kind));
        if ((k == k_subtitles) && ! a_.known (a_srclang))
            pick (nit_data_type, ed_50, "4.7.9 The track element", es_error, ec_element, "<TRACK> with KIND=subtitles requires SRCLANG");
        if (a_.known (a_label))
            if (a_.get_string (a_label).empty ())
                pick (nit_empty, ed_50, "4.7.9 The track element", es_error, ec_element, "If LABEL is present, it cannot be empty"); } }

void element::examine_video ()
{   examine_media_element (elem_video, "4.7.6 The video element", "<VIDEO...>");
    if (a_.known (a_autoplay)) pick (nit_autoplay, es_warning, ec_rudeness, "AUTOPLAY on <VIDEO> is unspeakable"); }
