/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2006
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    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, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    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
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* midnightcode-html.c :: Midnight Code HTML library			*/


#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <ctype.h>
#include <arpa/inet.h>

#include "preferences.h"
#include "midnightcode-list.h"
#include "midnightcode-html.h"


/* TODO: Re-write to be RFC compliant when ABNF library is working */

int
midnightcode_html_urltype(char *parse_src, int parse_len) {
  int ret_i;
  int slash_count;
  int dot_count;
  int dotslash;
  int slashdot;
  char * store_src;


  if(*parse_src == '/')
    return MIDNIGHTCODE_HTML_URL_TYPE_ABSOLUTE;

  if((store_src = strstr(parse_src, "://")) != NULL) {
    return MIDNIGHTCODE_HTML_URL_TYPE_URI;
  }

  if(!strncmp(parse_src, "..", 2)) {
    return MIDNIGHTCODE_HTML_URL_TYPE_RELATIVE;
  }

  slash_count = 0;
  dot_count = 0;
  dotslash = 0;
  slashdot = 0;
  ret_i = MIDNIGHTCODE_HTML_URL_TYPE_UNKNOWN;

  while(*parse_src != '\0' && parse_len > 0) {
    if(*parse_src == '/')
      slash_count++;
    if(*parse_src == '.')
      dot_count++;

    /* TODO: take into account ".." */
    if(dot_count && !slash_count)
      dotslash = 1;
    if(!dot_count && slash_count)
      slashdot = 1;

    parse_src++;
    parse_len--;
  }
  if(dot_count && slash_count && dotslash)
    return MIDNIGHTCODE_HTML_URL_TYPE_FULL;

  if((dot_count && !slash_count) ||
     (!dot_count && !slash_count) ||
     (!dot_count && slash_count) ||
     (dot_count && slash_count && slashdot))
    return MIDNIGHTCODE_HTML_URL_TYPE_RELATIVE;

  return ret_i;
}



int
midnightcode_html_urlproto(char *string_dst, char *parse_src, int parse_len) {
  char *store_src, *base_src;
  int run_len;


  base_src = parse_src;

  if((store_src = strstr(parse_src, "://")) == NULL)
    return -1;

  run_len = (int)(store_src - parse_src);
  if(run_len > parse_len)
    return -1;

  snprintf(string_dst, run_len + 1, "%s", base_src);
  return parse_len + 1;

  return 0;
}



int
midnightcode_html_urlbase(char *string_dst, char *parse_src, int parse_len,
                          int for_type) {
  char *store_src, *base_src;
  int store_len, run_len;


  base_src = parse_src;

  switch(for_type) {

    case MIDNIGHTCODE_HTML_URL_TYPE_RELATIVE:
      if((store_src = strstr(parse_src, "://")) != NULL) {
        parse_len = parse_len - ((int)(store_src - parse_src) + 3);
        parse_src = store_src + 3;
        base_src = parse_src;
      }
      store_len = parse_len;
      parse_src+= store_len;
      store_src = NULL;
      while(parse_len) {
        if(*parse_src == '?' || *parse_src == '&')
          store_src = parse_src;
        parse_len--;
        parse_src--;
      }
      if(store_src) {
        parse_len = (int)(store_src - base_src);
        parse_src = (char *)(base_src + parse_len);
      } else {
        parse_len = store_len;
        parse_src = (char *)(base_src + parse_len);
      }
      store_len = parse_len;
      while(parse_len) {
        if(*parse_src == '/') {
          snprintf(string_dst, parse_len + 1, "%s", base_src);
          return parse_len + 1;
        }
        parse_len--;
        parse_src--;
      }
      snprintf(string_dst, store_len + 1, "%s", base_src);
      return store_len + 1;
      break;

    case MIDNIGHTCODE_HTML_URL_TYPE_ABSOLUTE:
      if((store_src = strstr(parse_src, "://")) != NULL) {
        parse_len = parse_len - ((int)(store_src - parse_src) + 3);
        parse_src = store_src + 3;
        base_src = parse_src;
      }
      store_len = parse_len;
      run_len = 0;
      while(*parse_src != '\0') {
        if(*parse_src == '/' || *parse_src == '?' || *parse_src == '&') {
          run_len++;
          if(run_len > parse_len)
            run_len = parse_len;
          snprintf(string_dst, run_len, "%s", base_src);
          return parse_len + 1;
        }
        parse_src++;
        run_len++;
      }
      snprintf(string_dst, store_len + 1, "%s", base_src);
      return store_len + 1;
      break;

    default:
      return -1;
      break;
  }

  return -1;
}



int
midnightcode_html_tagbit(char * parse_src, char * store_src,
                         int tag_state, int equal_state,
                         struct midnightcode_html_tag_element_t ** tag_element,
                         struct midnightcode_html_tag_head_t * tag,
                         struct midnightcode_html_document_head_t * document) {
  int str_len;
  struct midnightcode_html_document_element_t * doc_element;
  struct midnightcode_html_tag_element_t * local_tag_element;
  struct midnightcode_html_tag_head_t * local_tag;
  midnightcode_list_init(html_tag_struct_head,
                         midnightcode_html_tag_element_t);


  if(!tag)
    return -1;
  if(!*tag_element && equal_state)
    return -1;
  if(!document && !tag_state)
    return -1;

  str_len = (int)(parse_src - store_src);

  if(!tag_state) {
    local_tag = malloc(sizeof(struct html_tag_struct_head));
    memset(local_tag, 0, sizeof(struct html_tag_struct_head));

    doc_element = malloc(sizeof(struct midnightcode_html_document_element_t));
    memset(doc_element, 0,
                         sizeof(struct midnightcode_html_document_element_t));
    doc_element->tag = local_tag;
    midnightcode_list_append(document, doc_element);

    local_tag_element = malloc(
                         sizeof(struct midnightcode_html_tag_element_t));
    memset(local_tag_element, 0,
                         sizeof(struct midnightcode_html_tag_element_t));
    midnightcode_list_append(local_tag, local_tag_element);

    local_tag_element->option = malloc(
                         strlen(MIDNIGHTCODE_HTML_TEXT_TAG) + 1);
    memset(local_tag_element->option, 0,
                         strlen(MIDNIGHTCODE_HTML_TEXT_TAG) + 1);
    strncpy(local_tag_element->option,
                         MIDNIGHTCODE_HTML_TEXT_TAG,
                         strlen(MIDNIGHTCODE_HTML_TEXT_TAG));

    local_tag_element->value = malloc(str_len + 1);
    memset(local_tag_element->value, 0, str_len + 1);
    strncpy(local_tag_element->value, store_src, str_len);

  } else {
    /* TODO:  <!--
       if count of tag == 0
       then first element (command elemet) == 1
    */
    if(str_len > 0) {
      if(!equal_state) {
        *tag_element = malloc(
                         sizeof(struct midnightcode_html_tag_element_t));
        memset(*tag_element, 0,
                         sizeof(struct midnightcode_html_tag_element_t));
        (*tag_element)->option = malloc(str_len + 1);
        memset((*tag_element)->option, 0, str_len + 1);
        strncpy((*tag_element)->option, store_src, str_len);
        midnightcode_list_append(tag, (*tag_element));

      } else {
        (*tag_element)->value = malloc(str_len + 1);
        memset((*tag_element)->value, 0, str_len + 1);
        strncpy((*tag_element)->value, store_src, str_len);

      }
    }
  }

  return str_len;
}



int
midnightcode_html_parse(char *parse_src, int parse_len,
                        struct midnightcode_html_document_head_t * document) {
  char *text_dst, *store_src;
  struct midnightcode_html_document_element_t * doc_element;
  struct midnightcode_html_tag_element_t * tag_element;
  struct midnightcode_html_tag_head_t * tag;
  int tag_state;
  int comment_state;
  int equal_state;
  int quote_state;
  int quote_type;
  int str_len;
  midnightcode_list_init(html_tag_struct_head,
                         midnightcode_html_tag_element_t);

  if(!document || !parse_src || parse_len < 1)
    return -1;

  store_src = parse_src;
  quote_type = 0;
  quote_state = 0;
  equal_state = 0;
  comment_state = 0;
  tag_state = 0;
  str_len = 0;
  text_dst = NULL;
  tag_element = NULL;
  tag = NULL;

  while(*parse_src != '\0' && parse_len > 0) {

    if(*parse_src == '<' && !quote_state && !comment_state) {
      if(tag_element) {
        str_len = midnightcode_html_tagbit(parse_src, store_src,
                  tag_state, equal_state, &tag_element, tag, document);
        if(str_len < 0)
          return -1;
      }
      tag = malloc(sizeof(struct html_tag_struct_head));
      memset(tag, 0, sizeof(struct html_tag_struct_head));

      doc_element = malloc(
                      sizeof(struct midnightcode_html_document_element_t));
      memset(doc_element, 0,
                      sizeof(struct midnightcode_html_document_element_t));
      doc_element->tag = tag;
      midnightcode_list_append(document, doc_element);
      store_src = parse_src + 1;

      tag_state = 1;
      equal_state = 0;
      quote_state = 0;

    } else if(*parse_src == '>' && tag_state && !quote_state) {
      /* TODO: -->
         if comment_state
           if *p-2 == - and *p-1 == - .. undo comment_state
      */
      str_len = midnightcode_html_tagbit(parse_src, store_src,
                  tag_state, equal_state, &tag_element, tag, document);
      if(str_len < 0)
        return -1;
      store_src = parse_src + 1;

      tag_state = 0;
      equal_state = 0;
      quote_state = 0;

    } else if(*parse_src == '\'' && tag_state &&
              (!quote_state || (quote_state && quote_type == 2))) {
      str_len = midnightcode_html_tagbit(parse_src, store_src,
                  tag_state, equal_state, &tag_element, tag, document);
      if(str_len < 0)
        return -1;
      store_src = parse_src + 1;
      quote_state = !quote_state;
      quote_type = 0;
      if(quote_state)
        quote_type = 2;

    } else if(*parse_src == '"' && tag_state && !comment_state &&
              (!quote_state || (quote_state && quote_type == 1))) {
      str_len = midnightcode_html_tagbit(parse_src, store_src,
                  tag_state, equal_state, &tag_element, tag, document);
      if(str_len < 0)
        return -1;
      store_src = parse_src + 1;
      quote_state = !quote_state;
      quote_type = 0;
      if(quote_state)
        quote_type = 1;

    } else if(tag_state && !quote_state && !comment_state &&
             (*parse_src == ' ' || *parse_src == '\t' ||
              *parse_src == '\r' || *parse_src == '\n' ||
              *parse_src < 32 || *parse_src > 126)) {
      str_len = midnightcode_html_tagbit(parse_src, store_src,
                  tag_state, equal_state, &tag_element, tag, document);
      if(str_len < 0)
        return -1;
      if(equal_state)
        equal_state = 0;
      store_src = parse_src + 1;
      
    } else if(*parse_src == '=' && tag_state && !quote_state &&
             !comment_state) {
      /* TODO: Consider the impact of equal_state = 1 passing thru here */
      str_len = midnightcode_html_tagbit(parse_src, store_src,
                  tag_state, equal_state, &tag_element, tag, document);
      if(str_len < 0)
        return -1;
      store_src = parse_src + 1;
      equal_state = 1;

    } else if(*(parse_src + 1) == '\0') {
      str_len = midnightcode_html_tagbit(parse_src, store_src,
                  tag_state, equal_state, &tag_element, tag, document);
      if(str_len < 0)
        return -1;
      store_src = parse_src + 1;

    }
    parse_src++;
    parse_len--;

  }

  return parse_len;
}

