#!/usr/bin/env python3

"""
Cuchulufm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.

Copyright (C) 2024 Antonio de la Rosa Caballero

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""


from collections import OrderedDict
from html import escape

#Forms para python3

class BaseForm:
    """The class used by all forms classes
    
    BaseForm is the base class used for all form classes.
    
    A form class is used for generate simple html forms, how input type, text type, hidden type, etc. PhangoField classes use this forms for generate automatically forms using GenerateAdminClass and others.
    """
    
    def __init__(self, name, value):
        """
        Args:
            name (str): The html name for this form
            value (str): The default value of this html form. 
            
        Attributes:
            label (str): Label used in functions how show_form that generate a complete html form from a form class list
            name (str): Name of the html form.
            default_value (mixed): The default value of the form. Equal to value in typical html form.
            css (str): Used for add css classes to the html form
            type (str): Variable used for conventional html forms with html tag <input>
            field (PhangoField): Field related with this form. Used in PhangoField. 
            required (boolean): If form is required or not, used in functions that generate forms.
            name_field_id (str): The html id for the html form. Used for html things.
            help (str): A string with help text, used in functions that generate forms.
        """
        
        self.label=name
        self.name=name
        self.default_value=value
        self.css=''
        self.type='text'
        self.field=None
        self.required=False
        self.txt_error=''
        self.error=False
        self.name_field_id=self.name+'_form'
        self.help=''
        self.placeholder=''
        
    def form(self):
        """Method for returm the html code of the form
        """
        
        return '<input type="'+self.type+'" class="'+self.css+'" name="'+self.name+'" id="'+self.name_field_id+'" value="'+self.setform(self.default_value)+'" placeholder="'+self.placeholder+'" />'
    
    def show_formatted(self, value):
        """Method for show the value of form formatted
        
        Args:
            value (mixed): The value of field form
        """
    
        return value
    
    #Method for escape value for html input. DON'T CHANGE IF YOU DON'T KNOWN WHAT ARE YOU DOING
    
    def setform(self, value):
        """A method for set the value in the form for escape and other things
        
        Args:
            value (mixed): The value of field form for set
        """
        
        value=str(value)
        
        return value.replace('"', '&quot;').replace("'", '&#39;')
    
    def change_name(self, new_name):
        """A method for change the default form html name of the field form
        
        Args:
            new_name (str): The new name of the form. Always is finished with _form suffix
        """
        
        self.name=new_name

        self.name_field_id=self.name+'_form'

        return ""

class SimpleTextForm(BaseForm):
    """Form for simple text
    """
    
    def __init__(self, name, value):
        super().__init__(name, value)
        
        self.after_text=''
    
    def form(self):
        
        return super().form()+' '+self.after_text

class TextForm(BaseForm):
    """Form for simple text form
    """
    
    def __init__(self, name, value):
        super(TextForm, self).__init__(name, value)
        
    def form(self):
        
        return '<textarea class="'+self.css+'" name="'+self.name+'" id="'+self.name+'_form">'+self.setform(self.default_value)+'</textarea>'

class PasswordForm(BaseForm):
    """Form for password forms
    """
    
    def __init__(self, name, value, show_password=False):
        super(PasswordForm, self).__init__(name, value)
        self.type='password'
        self.show_password=show_password
        
    def setform(self, value):
        if not self.show_password:
            return ""
            
        else:
            return value

class HiddenForm(BaseForm):
    """Form for hidden forms
    """
    
    def __init__(self, name, value):
        super(HiddenForm, self).__init__(name, value)
        self.type='hidden'


class SelectForm(BaseForm):
    """Form for select html form
    """
    
    def __init__(self, name, value, elements=OrderedDict()):
        """
        Args:
            name (str): The html name for this form
            value (str): The default value of this html form
            elements (OrderedDict): An ordered dict with the keys(the form value) and text label. Example, if you have a OrderedDict how {'0': 'Value selected'} in a html select form you have the next result: <select name="name"><option value="0">Value selected</option></selected>
        """
        super(SelectForm, self).__init__(name, value)
        self.arr_select=elements
    
    def form(self):
        
        the_form='<select name="'+self.name+'" id="'+self.name_field_id+'">\n'
        
        arr_selected={self.default_value: 'selected'}
        
        for k,v in self.arr_select.items():
            arr_selected[k]=arr_selected.get(k, '')
            
            the_form+="<option value=\""+self.setform(str(k))+"\" "+arr_selected[k]+">"+self.setform(str(v))+"</option>"
        
        the_form+='</select>\n'
        
        return the_form

class SelectModelForm(SelectForm):
    """Form for select html using a webmodel how base for get the data
    """
    
    def __init__(self, name, value, model, field_name, field_value, field_parent=None):
        """
        Args:
            name (str): The html name for this form
            value (str): The default value of this html form. 
            model (WebModel): The webmodel used for get the data for arr_select
            field_name (str): The field used for get the name of every option in select
            field_value (str): The field used for get the value of every option in select
            field_parent (int): If the model have parents or children, the value of this argument
        """
        super(SelectModelForm, self).__init__(name, value)
        
        try:
            self.default_value=int(self.default_value)
        except:
            self.default_value=0
            
        self.arr_select=OrderedDict()
        self.model=model
        self.field_name=field_name
        self.field_value=field_value
        self.field_parent=field_parent
        
        self.form=self.normal_form
        
        if self.field_parent!=None:
            self.form=self.parent_form
        
        
    def normal_form(self):
        """Method for prepare the form hierated from SelectForm class, without parents
        
        Method for prepare the form hierated from SelectForm class getting data from database using model attribute.
        """
        
        self.arr_select['']=''
        
        with self.model.select([self.field_name, self.field_value], True) as cur:        
            for arr_value in cur:
                #print(self.model.fields[self.field_name])
                self.arr_select[arr_value[self.field_value]]=self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])
                
            try:
            
                self.default_value=int(self.default_value)
                
            except:
                self.default_value=0
        
        return super().form()
        
    def parent_form(self):
        """Method for prepare the form hierated from SelectForm class, with parents
        
        Method for prepare the form hierated from SelectForm class getting data from database using model attribute.
        """
        
        self.arr_select['']=''
        
        arr_son={}
        
        old_conditions=self.model.conditions
        old_limit=self.model.limit
        
        self.model.limit=''
        
        self.model.set_conditions('WHERE 1=1', [])
        
        
        with self.model.select([self.field_name, self.field_value, self.field_parent], True) as cur:
        
            for arr_value in cur:
                
                if not arr_value[self.field_parent] in arr_son:
                    
                    arr_son[arr_value[self.field_parent]]=[]
                
                if arr_value[self.field_value]!=self.model.model_id:
                    arr_son[arr_value[self.field_parent]].append([arr_value[self.field_value], self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])])
        
        self.create_son(0, arr_son)
        
        self.model.conditions=old_conditions
        self.model.limit=old_limit
        
        try:
        
            self.default_value=int(self.default_value)
            
        except:
            self.default_value=0
        
        return super().form()
        

    def create_son(self, parent_id, arr_son, separator=''):
        """Recursive method for generate parents and children dictionary
        """
        
        if parent_id in arr_son:        
            for son in arr_son[parent_id]:
                self.arr_select[son[0]]=separator+son[1]
                
                son_separator=separator
                
                if son[0] in arr_son:    
                    son_separator+='--'
                    self.create_son(son[0],arr_son, son_separator)
