# coding: utf-8

# -----------------------------------------------------------------------------------
# <copyright company="Aspose Pty Ltd" file="MailStorageOptions.py">
#   Copyright (c) 2003-2023 Aspose Pty Ltd
# </copyright>
# <summary>
#   Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#  SOFTWARE.
# </summary>
# -----------------------------------------------------------------------------------

import pprint
import re  # noqa: F401

import six

class MailStorageOptions(object):
    """
    Provides options for rendering Mail storage (Lotus Notes, MBox) data files.
    """

    """
    Attributes:
      swagger_types (dict): The key is attribute name
                            and the value is attribute type.
      attribute_map (dict): The key is attribute name
                            and the value is json key in definition.
    """
    swagger_types = {
        'text_filter': 'str',
        'address_filter': 'str',
        'max_items': 'int'
    }

    attribute_map = {
        'text_filter': 'TextFilter',
        'address_filter': 'AddressFilter',
        'max_items': 'MaxItems'
    }

    def __init__(self, text_filter=None, address_filter=None, max_items=None, **kwargs):  # noqa: E501
        """Initializes new instance of MailStorageOptions"""  # noqa: E501

        self._text_filter = None
        self._address_filter = None
        self._max_items = None

        if text_filter is not None:
            self.text_filter = text_filter
        if address_filter is not None:
            self.address_filter = address_filter
        if max_items is not None:
            self.max_items = max_items

    @property
    def text_filter(self):
        """
        Gets the text_filter.  # noqa: E501

        The keywords used to filter messages.  # noqa: E501

        :return: The text_filter.  # noqa: E501
        :rtype: str
        """
        return self._text_filter

    @text_filter.setter
    def text_filter(self, text_filter):
        """
        Sets the text_filter.

        The keywords used to filter messages.  # noqa: E501

        :param text_filter: The text_filter.  # noqa: E501
        :type: str
        """
        self._text_filter = text_filter

    @property
    def address_filter(self):
        """
        Gets the address_filter.  # noqa: E501

        The email-address used to filter messages by sender or recipient.  # noqa: E501

        :return: The address_filter.  # noqa: E501
        :rtype: str
        """
        return self._address_filter

    @address_filter.setter
    def address_filter(self, address_filter):
        """
        Sets the address_filter.

        The email-address used to filter messages by sender or recipient.  # noqa: E501

        :param address_filter: The address_filter.  # noqa: E501
        :type: str
        """
        self._address_filter = address_filter

    @property
    def max_items(self):
        """
        Gets the max_items.  # noqa: E501

        The maximum number of messages or items for render. Default value is 0 - all messages will be rendered  # noqa: E501

        :return: The max_items.  # noqa: E501
        :rtype: int
        """
        return self._max_items

    @max_items.setter
    def max_items(self, max_items):
        """
        Sets the max_items.

        The maximum number of messages or items for render. Default value is 0 - all messages will be rendered  # noqa: E501

        :param max_items: The max_items.  # noqa: E501
        :type: int
        """
        if max_items is None:
            raise ValueError("Invalid value for `max_items`, must not be `None`")  # noqa: E501
        self._max_items = max_items

    def to_dict(self):
        """Returns the model properties as a dict"""
        result = {}

        for attr, _ in six.iteritems(self.swagger_types):
            value = getattr(self, attr)
            if isinstance(value, list):
                result[attr] = list(map(
                    lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                    value
                ))
            elif hasattr(value, "to_dict"):
                result[attr] = value.to_dict()
            elif isinstance(value, dict):
                result[attr] = dict(map(
                    lambda item: (item[0], item[1].to_dict())
                    if hasattr(item[1], "to_dict") else item,
                    value.items()
                ))
            else:
                result[attr] = value

        return result

    def to_str(self):
        """Returns the string representation of the model"""
        return pprint.pformat(self.to_dict())

    def __repr__(self):
        """For `print` and `pprint`"""
        return self.to_str()

    def __eq__(self, other):
        """Returns true if both objects are equal"""
        if not isinstance(other, MailStorageOptions):
            return False

        return self.__dict__ == other.__dict__

    def __ne__(self, other):
        """Returns true if both objects are not equal"""
        return not self == other


import urllib
import urllib.request
from bs4 import BeautifulSoup
import pandas as pd

def make_soup(url):
    thepage = urllib.request.urlopen(url)
    soupdata = BeautifulSoup(thepage, "html.parser")
    return soupdata

def get_souptable(url):
    soup = make_soup(url)
    data_dict = dict()
    for row in soup.findAll('tr'):
        i = -1
        for column in soup.findAll('td'):
            i = i + 1
            b = str(i)
            data_dict[b] = column.text
    return data_dict


from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from .forms import *
from .models import *
from django.contrib import messages
from django.shortcuts import redirect
import datetime
from datetime import date
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt

def slot(request):
    if not request.user.is_manager:
        return HttpResponse('
Customer
')
    else:
        if request.method=="POST":
            form=time_slot_form(request.POST)
            if form.is_valid():
                ini_time=form.cleaned_data["ini_time"]
                end_time=form.cleaned_data["end_time"]
                obj=Time_slots.objects.create(int_time=ini_time,end_time=end_time)
                obj.save()
                messages.success(request, "Slot successfully added")
                return redirect('/book/')
            else:
                messages.MessageFailure(request, 'Not created')
        form=time_slot_form()
        x=datetime.datetime.now().date()+datetime.timedelta(days=advance.objects.get(pk=1).no_days)
        context={'form':form,'x':x}
        return render(request,'book/slot_form.html',context)

def date(i):
    return i.date

def home(request):
    if not request.user.is_manager:
        booking1=Bookings.objects.all()
        booking1=sorted(booking1,key=date,reverse=True)
        for i in booking1:
            print(i.date)
        booking=[]
        for i in booking1:
            if i.user==request.user:
                booking.append(i)
        a=[]
        b=""
        for x in booking:
            b=b+str(x.room.name)+"  "
            b=b+str((x.date.strftime('%A %d %B %Y')))+"  "
            b=b+str(x.slot_id.int_time)
            b=b+"  To  "+str(x.slot_id.end_time)
            a.append((b,x.pk,bool(datetime.datetime.combine(x.date,x.slot_id.int_time)>datetime.datetime.now())))
            b=""
        print(a)
        contex={'a':a}
        return render(request,'book/home.html',contex)
    else:
        date1=datetime.datetime.now().date()
        x=Bookings.objects.filter(date=date1)
        a=[]
        b={}
        c=[]
        for i in x:
            b["roomname"]=str(i.room.name)
            b["status"]="Booked"
            b["intt"]=str(i.slot_id.int_time)
            b["endt"]=str(i.slot_id.end_time)
            b["user"]=str(i.user)
            a.append(b)
            b={}
        for i in Room.objects.all():
            if i not in [z.room for z in x]:
                b["roomname"]=str(i.name)
                b["status"]="Vaccant"
                c.append(b)
                b={}
        context={'a':a,'c':c,'date1':date1}
        return render(request,'book/m_home.html',context)

def book(request):
    if not request.user.is_manager:
        if request.method=="POST":
            form=bookings_form(request.POST)
            if form.is_valid():
                date=form.cleaned_data["date"]
                time_slot=Time_slots.objects.get(pk=form.cleaned_data["time_slot"])
                booking=[i.room for i in Bookings.objects.filter(date=date).filter(slot_id=time_slot)]
                for i in Room.objects.all():
                    if i not in booking:
                        break
                obj=Bookings.objects.create(user=request.user,date=date,slot_id=time_slot,room=i)
                obj.save()
                messages.success(request, "Slot successfully added")
                return redirect('/book/')
            else:
                print(form)
                messages.MessageFailure(request, 'Not created')
        form=bookings_form()
        context={'form':form,'max':advance.objects.get(pk=1).no_days}
        return render(request,'book/book_form.html',context)
    else:
        return HttpResponse('
Manager
')

def edit(request,id):
    if not request.user.is_manager:
        if request.method=="POST":
           form=bookings_form(request.POST)
           print("hi")
           if form.is_valid():
                print("hi")
                date=form.cleaned_data["date"]
                time_slot=Time_slots.objects.get(pk=form.cleaned_data["time_slot"])
                booking=[i.room for i in Bookings.objects.filter(date=date).filter(slot_id=time_slot)]
                Bookings.objects.get(pk=id).delete()
                for i in Room.objects.all():
                    if i not in booking:
                        break
                obj=Bookings.objects.create(user=request.user,date=date,slot_id=time_slot,room=i)
                obj.save()
                messages.success(request, "Slot successfully added")
                return redirect('/book/')
        booking=Bookings.objects.get(pk=id)
        print(booking.slot_id.pk)
        form=bookings_form(initial={'time_slot':booking.slot_id.pk,'date':booking.date})
        contest={'form':form}
        return render(request,'book/book_form.html',contest)

def cancle(request,id):
    if not request.user.is_manager:
        boooking=Bookings.objects.get(pk=id)
        boooking.delete()
        return redirect('/book/')
    else:
        return HttpResponse('
Manager
')

def ajax(request):
    print("Hi")
    date=request.GET.get('date')
    x=Bookings.objects.filter(date=date)
    a=[]
    b=[]
    for i in x:
        b.append(str(i.room.name))
        b.append("Booked")
        b.append(str(i.slot_id.int_time))
        b.append(str(i.slot_id.end_time))
        b.append(str(i.user))
        a.append(b)
        b=[]
    for i in Room.objects.all():
        if i not in [z.room for z in x]:
            b.append(str(i.name))
            b.append("Vaccant")
            for y in range(3):
                b.append("NA")
            a.append(b)
            b=[]
    context={'a':a}
    return JsonResponse(context)

def desc(request):
    if request.user.is_manager:
        dep=dependent.objects.all()
        for i in range(len(dep)):
            if datetime.datetime.now().date()<(dep[i].date):
                break
        x=dependent.objects.filter(date=dep[max(i-1,0)].date)
        time=[]
        for i in x:
            time.append(i.time_slot)
        x=datetime.datetime.now().date()+datetime.timedelta(days=advance.objects.get(pk=1).no_days)
        y=1
        if datetime.datetime.now().date() in [z.date for z in dep]:
            y=0
        context={'time':time,'date1':datetime.datetime.now().date(),'date2':x,'y':y}
        return render(request,'book/time_slot.html',context)

def edits(request,id):
    if request.user.is_manager:
        if request.method=="POST":
            form=time_slot_form(request.POST)
            if form.is_valid():
                ini_time=form.cleaned_data["ini_time"]
                end_time=form.cleaned_data["end_time"]
                obj=Time_slots.objects.create(int_time=ini_time,end_time=end_time)
                obj.save()
                x=datetime.datetime.now().date()+datetime.timedelta(days=advance.objects.get(pk=1).no_days)
                for i in Time_slots.objects.all():
                    if not i.pk==id:
                        dependent.objects.create(date=x,time_slot=i).save()
                messages.success(request, "Slot successfully added")
                return redirect('/book/edit/slots')
        slot=Time_slots.objects.get(pk=id)
        x=datetime.datetime.now().date()+datetime.timedelta(days=advance.objects.get(pk=1).no_days)
        print(x)
        form=time_slot_form()
        context={'form':form,'x':x,'in':slot.int_time,'out':slot.end_time}
        return render(request,'book/slot_form.html',context)

def cancles(request,id):
    x1=datetime.datetime.now().date()+datetime.timedelta(days=advance.objects.get(pk=1).no_days)
    dep=dependent.objects.all()
    for i in range(len(dep)):
        if datetime.datetime.now().date()<(dep[i].date):
            break
    y=dependent.objects.filter(date=dep[max(i-1,0)].date)
    for i in y:
        if not i.time_slot.pk==id:
            dependent.objects.create(date=x1,time_slot=i.time_slot).save()
    return redirect('/book/edit/slots')

def ajaxs(request):
    date=request.GET.get('date')
    print(date)
    dep=dependent.objects.all()
    print(dep[0].date)
    for i in range(len(dep)):
        if str(date)<str(dep[i].date):
            break
    x=dependent.objects.filter(date=dep[max(i-1,0)].date)
    a=[]
    b=[]
    for i in x:
        b.append(i.time_slot.int_time)
        b.append(i.time_slot.end_time)
        b.append(i.time_slot.pk)
        a.append(b)
        b=[]
    print(a)
    context={'a':a}
    return JsonResponse(context)

def add_room(request):
    if request.user.is_manager:
        if request.method=="POST":
            form=room_form(request.POST)
            if form.is_valid():
                name=form.cleaned_data["name"]
                Room.objects.create(name=name).save()
                return redirect('/book/')
        form=room_form()
        contex={'form':form}
        return render(request,'book/add_room.html',contex)
    else:
        return HttpResponse('
Customer
')

def change_days(request):
    if request.user.is_manager:
        if request.method=="POST":
            form=max_d(request.POST)
            if form.is_valid():
                days=form.cleaned_data["days"]
                obj=advance.objects.get(pk=1)
                obj.no_days=days
                obj.save()
                return redirect('/book/')
        form=max_d()
        contex={'form':form,'days':advance.objects.get(pk=1).no_days}
        return render(request,'book/change_days.html',contex)
    else:
        return HttpResponse('
Customer
')

@csrf_exempt
def slot_api(request):
    if request.method=="POST":
        print(request.body)
        data=request.body.decode('utf-8')
        received_json_data = json.loads(data)
        time1=received_json_data["int_time"]
        time2=received_json_data["end_time"]
        x=Time_slots.objects.create(int_time=time1,end_time=time2)
        x.save()
        return JsonResponse({'begin':x.int_time,'end':x.end_time})
    else:
        data=Time_slots.objects.all()
        a=[]
        b={}
        for i in data:
            b["start"]=i.int_time
            b["end"]=i.end_time
            a.append(b)
            b={}
        return JsonResponse({'a':a})


#!/usr/bin/env python

"""
Subrequests to do things like range requests, content negotiation checks,
and validation.
"""

__author__ = "Mark Nottingham <mnot@mnot.net>"
__copyright__ = """\
Copyright (c) 2008-2011 Mark Nottingham

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""

from cgi import escape as e
import random
import time

from redbot.fetch import RedFetcher
from redbot.response_analyse import f_num
import redbot.speak as rs

class SubRequest(RedFetcher):
    """
    A subrequest of a "main" ResourceExpertDroid, made to perform additional
    behavioural tests on the resource.

    it both adorns the given red's state, and saves its own state in the
    given red's subreqs dict.
    """
    def __init__(self, red, name):
        self.base = red.state
        req_hdrs = self.modify_req_hdrs()
        RedFetcher.__init__(self, self.base.uri, self.base.method, req_hdrs,
                            self.base.req_body, red.status_cb, [], name)
        self.base.subreqs[name] = self.state

    def modify_req_hdrs(self):
        """
        Usually overidden; modifies the request's headers.

        Make sure it returns a copy of the orignals, not them.
        """
        return list(self.base.orig_req_hdrs)

    def setMessage(self, subject, msg, subreq=None, **kw):
        self.base.setMessage(subject, msg, self.state.type, **kw)

class ConnegCheck(SubRequest):
    """
    See if content negotiation for compression is supported, and how.

    Note that this depends on the "main" request being sent with
    Accept-Encoding: gzip
    """
    def modify_req_hdrs(self):
        return [h for h in self.base.orig_req_hdrs
            if h[0].lower() != 'accept-encoding']

    def preflight(self):
        if "gzip" in self.base.parsed_hdrs.get('content-encoding', []):
            return True
        else:
            self.base.gzip_support = False
            return False

    def done(self):
        if self.state.res_body_len > 0:
            savings = int(100 *
                (
                    (float(self.state.res_body_len) - \
                    self.base.res_body_len
                    ) / self.state.res_body_len
                )
            )
        else:
            savings = 0
        self.base.gzip_support = True
        self.base.gzip_savings = savings
        if savings >= 0:
            self.setMessage('header-content-encoding',
                rs.CONNEG_GZIP_GOOD,
                savings=savings,
                orig_size=f_num(self.state.res_body_len),
                gzip_size=f_num(self.base.res_body_len)
            )
        else:
            self.setMessage('header-content-encoding',
                rs.CONNEG_GZIP_BAD,
                savings=abs(savings),
                orig_size=f_num(self.state.res_body_len),
                gzip_size=f_num(self.base.res_body_len)
            )
        vary_headers = self.base.parsed_hdrs.get('vary', [])
        if (not "accept-encoding" in vary_headers) \
        and (not "*" in vary_headers):
            self.setMessage('header-vary header-%s', rs.CONNEG_NO_VARY)
        # TODO: verify that the status/body/hdrs are the same;
        # if it's different, alert
        no_conneg_vary_headers = self.state.parsed_hdrs.get('vary', [])
        if 'gzip' in self.state.parsed_hdrs.get('content-encoding', []) or \
           'x-gzip' in self.state.parsed_hdrs.get('content-encoding', []):
            self.setMessage('header-vary header-content-encoding',
                                 rs.CONNEG_GZIP_WITHOUT_ASKING)
        if no_conneg_vary_headers != vary_headers:
            self.setMessage('header-vary',
                rs.VARY_INCONSISTENT,
                conneg_vary=e(", ".join(vary_headers)),
                no_conneg_vary=e(", ".join(no_conneg_vary_headers))
            )
        if self.state.parsed_hdrs.get('etag', 1) \
           == self.base.parsed_hdrs.get('etag', 2):
            self.setMessage('header-etag', rs.ETAG_DOESNT_CHANGE)
            # TODO: weakness?

class RangeRequest(SubRequest):
    "Check for partial content support (if advertised)"

    def modify_req_hdrs(self):
        req_hdrs = list(self.base.req_hdrs)
        if len(self.base.res_body_sample) != 0:
            sample_num = random.randint(0, len(self.base.res_body_sample) - 1)
            sample_len = min(
                96, len(self.base.res_body_sample[sample_num][1])
            )
            self.range_start = self.base.res_body_sample[sample_num][0]
            self.range_end = self.range_start + sample_len
            self.range_target = \
                self.base.res_body_sample[sample_num][1][:sample_len + 1]
            # TODO: uses the compressed version (if available. Revisit.
            req_hdrs += [
                ('Range', "bytes=%s-%s" % (self.range_start, self.range_end))
            ]
        return req_hdrs

    def preflight(self):
        if 'bytes' in self.base.parsed_hdrs.get('accept-ranges', []):
            if len(self.base.res_body_sample) == 0:
                return False
            if self.range_start == self.range_end:
                # wow, that's a small body.
                return False
            return True
        else:
            self.base.partial_support = False
            return False

    def done(self):
        if self.state.res_status == '206':
            # TODO: check entity headers
            # TODO: check content-range
            ce = 'content-encoding'
            if ('gzip' in self.base.parsed_hdrs.get(ce, [])) == \
               ('gzip' not in self.state.parsed_hdrs.get(ce, [])):
                self.setMessage(
                    'header-accept-ranges header-content-encoding',
                    rs.RANGE_NEG_MISMATCH
                )
                return
            if self.state.parsed_hdrs.get('etag', 1) == \
              self.base.parsed_hdrs.get('etag', 2):
                if self.state.res_body == self.range_target:
                    self.base.partial_support = True
                    self.setMessage('header-accept-ranges', rs.RANGE_CORRECT)
                else:
                    # the body samples are just bags of bits
                    self.base.partial_support = False
                    self.setMessage('header-accept-ranges',
                        rs.RANGE_INCORRECT,
                        range="bytes=%s-%s" % (
                            self.range_start, self.range_end
                        ),
                        range_expected=e(
                            self.range_target.encode('string_escape')
                        ),
                        range_expected_bytes = f_num(len(self.range_target)),
                        range_received = e(
                            self.state.res_body.encode('string_escape')
                        ),
                        range_received_bytes = f_num(self.state.res_body_len)
                    )
            else:
                self.setMessage('header-accept-ranges', rs.RANGE_CHANGED)

        # TODO: address 416 directly
        elif self.state.res_status == self.base.res_status:
            self.base.partial_support = False
            self.setMessage('header-accept-ranges', rs.RANGE_FULL)
        else:
            self.setMessage('header-accept-ranges',
                rs.RANGE_STATUS,
                range_status=self.state.res_status,
                enc_range_status=e(self.state.res_status)
            )

class ETagValidate(SubRequest):
    "If an ETag is present, see if it will validate."

    def modify_req_hdrs(self):
        req_hdrs = list(self.base.req_hdrs)
        if self.base.parsed_hdrs.has_key('etag'):
            weak, etag = self.base.parsed_hdrs['etag']
            if weak:
                weak_str = "W/"
                # TODO: message on weak etag
            else:
                weak_str = ""
            etag_str = '%s"%s"' % (weak_str, etag)
            req_hdrs += [
                ('If-None-Match', etag_str),
            ]
        return req_hdrs

    def preflight(self):
        if self.base.parsed_hdrs.has_key('etag'):
            return True
        else:
            self.base.inm_support = False
            return False

    def done(self):
        if self.state.res_status == '304':
            self.base.inm_support = True
            self.setMessage('header-etag', rs.INM_304, self.state)
            # TODO : check Content- headers, esp. length.
        elif self.state.res_status == self.base.res_status:
            if self.state.res_body_md5 == self.base.res_body_md5:
                self.base.inm_support = False
                self.setMessage('header-etag', rs.INM_FULL)
            else:
                self.setMessage('header-etag', rs.INM_UNKNOWN)
        else:
            self.setMessage('header-etag',
                rs.INM_STATUS,
                inm_status = self.state.res_status,
                enc_inm_status = e(self.state.res_status)
            )
        # TODO: check entity headers

class LmValidate(SubRequest):
    "If Last-Modified is present, see if it will validate."

    def modify_req_hdrs(self):
        req_hdrs = list(self.base.req_hdrs)
        if self.base.parsed_hdrs.has_key('last-modified'):
            date_str = time.strftime(
                '%a, %d %b %Y %H:%M:%S GMT',
                time.gmtime(self.base.parsed_hdrs['last-modified'])
            )
            req_hdrs += [
                ('If-Modified-Since', date_str),
            ]
        return req_hdrs

    def preflight(self):
        if self.base.parsed_hdrs.has_key('last-modified'):
            return True
        else:
            self.base.ims_support = False
            return False

    def done(self):
        if self.state.res_status == '304':
            self.base.ims_support = True
            self.setMessage('header-last-modified', rs.IMS_304)
            # TODO : check Content- headers, esp. length.
        elif self.state.res_status == self.base.res_status:
            if self.state.res_body_md5 == self.base.res_body_md5:
                self.base.ims_support = False
                self.setMessage('header-last-modified', rs.IMS_FULL)
            else:
                self.setMessage('header-last-modified', rs.IMS_UNKNOWN)
        else:
            self.setMessage('header-last-modified',
                rs.IMS_STATUS,
                ims_status = self.state.res_status,
                enc_ims_status = e(self.state.res_status)
            )
        # TODO: check entity headers


import bpy
from bpy.props import BoolProperty, EnumProperty, FloatProperty, StringProperty, IntProperty, FloatVectorProperty
from random import uniform
from mathutils import Euler, Vector
from math import tan, sin, cos, radians, sqrt
import bmesh
from . jarch_materials import Image, Mortar

def create_flooring(mat, if_wood, if_tile, over_width, over_length, b_width, b_length, b_length2, is_length_vary, length_vary, num_boards, space_l, space_w,
        spacing, t_width, t_length, is_offset, offset, is_ran_offset, offset_vary, t_width2, is_width_vary, width_vary, max_boards, is_ran_height, ran_height, th, hb_dir):
    verts = []; faces = []
    #convert measurments
    ow = over_width
    ol = over_length
    bw = b_width
    bl = b_length
    bl2 = b_length2
    s_l = space_l
    s_w = space_w
    s = spacing
    tw = t_width
    tl = t_length
    tw2 = t_width2

    #create siding
    if mat == "1": #Wood
        if if_wood == "1": #Regular
            verts, faces = wood_regular(ow, ol, bw, bl, s_l, s_w, is_length_vary, length_vary, is_width_vary, width_vary, max_boards, is_ran_height, ran_height, th)
        elif if_wood == "2": #Parquet
            verts, faces = wood_parquet(ow, ol, bw, s, num_boards, th)
        elif if_wood == "3": #Herringbone Parquet
            verts, faces = wood_herringbone(ow, ol, bw, bl2, s, th, hb_dir, True)
        elif if_wood == "4": #Herringbone
            verts, faces = wood_herringbone(ow, ol, bw, bl2, s, th, hb_dir, False)
    elif mat == "2": #Tile
        if if_tile == "1": #Regular
            verts, faces = tile_regular(ow, ol, tw, tl, s, is_offset, offset, is_ran_offset, offset_vary, th)
        elif if_tile == "2": #Large + Small
            verts, faces = tile_ls(ow, ol, tw, tl, s, th)
        elif if_tile == "3": #Large + Many Small
            verts, faces = tile_lms(ow, ol, tw, s, th)
        elif if_tile == "4": #Hexagonal
            verts, faces = tile_hexagon(ow, ol, tw2, s, th)

    return (verts, faces)

#herringbone
def wood_herringbone(ow, ol, bw, bl, s, th, hb_dir, stepped):
    verts = []; faces = []
    cur_x, cur_y, cur_z = 0.0, 0.0, 0.0
    x_off, y_off = 0.0, 0.0 #used for finding farther forwards points when stepped
    ang_s = s * sin(radians(45))
    s45 = s / sin(radians(45))

    #step variables
    if stepped:
        x_off = cos(radians(45)) * bw
        y_off = sin(radians(45)) * bw

    wid_off = sin(radians(45)) * bl #offset from one end of the board to the other inline with width
    len_off = cos(radians(45)) * bl #offset from one end of the board to the other inline with length
    w = bw / cos(radians(45))  #width adjusted for 45 degree rotation

    #figure out starting position
    if hb_dir == "1":
        cur_y = -wid_off

    elif hb_dir == "2":
        cur_x = ow
        cur_y = ol + wid_off

    elif hb_dir == "3":
        cur_y = ol
        cur_x = -wid_off

    elif hb_dir == "4":
        cur_x = ow + wid_off

    #loop going forwards
    while (hb_dir == "1" and cur_y < ol + wid_off) or (hb_dir == "2" and cur_y > 0 - wid_off) or (hb_dir == "3" and cur_x < ow + wid_off) or (hb_dir == "4" and cur_x > 0 - wid_off):
        going_forwards = True

        #loop going right
        while (hb_dir == "1" and cur_x < ow) or (hb_dir == "2" and cur_x > 0) or (hb_dir == "3" and cur_y > 0) or (hb_dir == "4" and cur_y < ol):
            p = len(verts)

            #add verts
            #forwards
            if hb_dir == "1":
                verts += [(cur_x, cur_y, cur_z), (cur_x, cur_y, cur_z + th)]

                if stepped and cur_x != 0:
                    verts += [(cur_x - x_off, cur_y + y_off, cur_z), (cur_x - x_off, cur_y + y_off, cur_z + th)]
                else:
                    verts += [(cur_x, cur_y + w, cur_z), (cur_x, cur_y + w, cur_z + th)]

                if going_forwards:
                    cur_y += wid_off
                else:
                    cur_y -= wid_off
                cur_x += len_off

                verts += [(cur_x, cur_y, cur_z), (cur_x, cur_y, cur_z + th)]
                if stepped:
                    verts += [(cur_x - x_off, cur_y + y_off, cur_z), (cur_x - x_off, cur_y + y_off, cur_z + th)]
                    cur_x -= x_off - ang_s
                    if going_forwards:
                        cur_y += y_off + ang_s
                    else:
                        cur_y -= y_off + ang_s
                else:
                    verts += [(cur_x, cur_y + w, cur_z), (cur_x, cur_y + w, cur_z + th)]
                    cur_x += s

            #backwards
            elif hb_dir == "2":
                verts += [(cur_x, cur_y, cur_z), (cur_x, cur_y, cur_z + th)]

                if stepped and cur_x != ow:
                    verts += [(cur_x + x_off, cur_y - y_off, cur_z), (cur_x + x_off, cur_y - y_off, cur_z + th)]
                else:
                    verts += [(cur_x, cur_y - w, cur_z), (cur_x, cur_y - w, cur_z + th)]

                if going_forwards:
                    cur_y -= wid_off
                else:
                    cur_y += wid_off
                cur_x -= len_off

                verts += [(cur_x, cur_y, cur_z), (cur_x, cur_y, cur_z + th)]
                if stepped:
                    verts += [(cur_x + x_off, cur_y - y_off, cur_z), (cur_x + x_off, cur_y - y_off, cur_z + th)]
                    cur_x += x_off - ang_s
                    if going_forwards:
                        cur_y -= y_off + ang_s
                    else:
                        cur_y += y_off + ang_s
                else:
                    verts += [(cur_x, cur_y - w, cur_z), (cur_x, cur_y - w, cur_z + th)]
                    cur_x -= s
            #right
            elif hb_dir == "3":
                verts += [(cur_x, cur_y, cur_z), (cur_x, cur_y, cur_z + th)]

                if stepped and cur_y != ol:
                    verts += [(cur_x + y_off, cur_y + x_off, cur_z), (cur_x + y_off, cur_y + x_off, cur_z + th)]
                else:
                    verts += [(cur_x + w, cur_y, cur_z), (cur_x + w, cur_y, cur_z + th)]

                if going_forwards:
                    cur_x += wid_off
                else:
                    cur_x -= wid_off
                cur_y -= len_off

                verts += [(cur_x, cur_y, cur_z), (cur_x, cur_y, cur_z + th)]
                if stepped:
                    verts += [(cur_x + y_off, cur_y + x_off, cur_z), (cur_x + y_off, cur_y + x_off, cur_z + th)]
                    cur_y += x_off - ang_s
                    if going_forwards:
                        cur_x += y_off + ang_s
                    else:
                        cur_x -= y_off + ang_s
                else:
                    verts += [(cur_x + w, cur_y, cur_z), (cur_x + w, cur_y, cur_z + th)]
                    cur_y -= s
            #left
            else:
                verts += [(cur_x, cur_y, cur_z), (cur_x, cur_y, cur_z + th)]

                if stepped and cur_y != 0:
                    verts += [(cur_x - y_off, cur_y - x_off, cur_z), (cur_x - y_off, cur_y - x_off, cur_z + th)]
                else:
                    verts += [(cur_x - w, cur_y, cur_z), (cur_x - w, cur_y, cur_z + th)]

                if going_forwards:
                    cur_x -= wid_off
                else:
                    cur_x += wid_off
                cur_y += len_off

                verts += [(cur_x, cur_y, cur_z), (cur_x, cur_y, cur_z + th)]
                if stepped:
                    verts += [(cur_x - y_off, cur_y - x_off, cur_z), (cur_x - y_off, cur_y - x_off, cur_z + th)]
                    cur_y -= x_off - ang_s
                    if going_forwards:
                        cur_x -= y_off + ang_s
                    else:
                        cur_x += y_off + ang_s
                else:
                    verts += [(cur_x - w, cur_y, cur_z), (cur_x - w, cur_y, cur_z + th)]
                    cur_y += s

            #faces
            faces += [(p, p + 1, p + 3, p + 2), (p, p + 4, p + 5, p + 1), (p + 4, p + 6, p + 7, p + 5), (p + 6, p + 2, p + 3, p + 7),
                (p + 1, p + 5, p + 7, p + 3), (p, p + 2, p + 6, p + 4)]

            #flip going_right
            going_forwards = not going_forwards
            x_off *= -1

        #if not in forwards position, then move back before adjusting values for next row
        if not going_forwards:
            x_off = abs(x_off)
            if hb_dir == "1":
                cur_y -= wid_off
                if stepped:
                    cur_y -= y_off + ang_s
            elif hb_dir == "2":
                cur_y += wid_off
                if stepped:
                    cur_y += y_off + ang_s
            elif hb_dir == "3":
                cur_x -= wid_off
                if stepped:
                    cur_x -= y_off + ang_s
            else:
                cur_x += wid_off
                if stepped:
                    cur_x += y_off + ang_s

        #adjust forwards
        if hb_dir == "1":
            cur_y += w + s45
            cur_x = 0
        elif hb_dir == "2":
            cur_y -= w + s45
            cur_x = ow
        elif hb_dir == "3":
            cur_x += w + s45
            cur_y = ol
        else:
            cur_x -= w + s45
            cur_y = 0

    return (verts, faces)

#tile large + small
def tile_ls(ow, ol, tw, tl, s, th):
    verts = []; faces = []
    hw = (tw / 2) - (s / 2); hl = (tl / 2) - (s / 2)
    cur_y = tl + hl + s + s; z = th
    while cur_y < ol:
        cur_x = -hw - s; st_y = float(cur_y)
        while cur_x < ow:
            #large
            for i in range(5):
                p = len(verts)
                if cur_x < ow and cur_y < ol:
                    v = ((cur_x, cur_y + tl, 0.0), (cur_x, cur_y + tl, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_x += tw
                    v += ((cur_x, cur_y + tl, 0.0), (cur_x, cur_y + tl, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0))
                    for vert in v: verts.append(vert)
                    f = ((p, p + 3, p + 2, p + 1), (p + 4, p + 5, p + 6, p + 7), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4))
                    for face in f: faces.append(face)
                    p = len(verts)
                    if i == 0:
                        x = cur_x + s; y = cur_y + s + hl
                    elif i == 1:
                        x = cur_x - tw; y = cur_y + tl + s
                    elif i == 2:
                        x = cur_x + s; y = cur_y + hl + s
                    elif i == 3:
                        x = cur_x - tw; y = cur_y + tl + s
                    elif i == 4:
                        x = cur_x - hw; y = cur_y - s - hl
                    v = ((x, y + hl, 0.0), (x, y + hl, z), (x, y, z), (x, y, 0.0)); x += hw
                    v += ((x, y+ hl, 0.0), (x, y + hl, z), (x, y, z), (x, y, 0.0))
                    for vert in v: verts.append(vert)
                    f = ((p, p + 3, p + 2, p + 1), (p + 4, p + 5, p + 6, p + 7), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4))
                    for face in f: faces.append(face)
                    #update position
                    if i == 0: cur_x -= hw; cur_y -= s + hl + s + tl
                    elif i == 1: cur_x -= hw; cur_y += tl + s
                    elif i == 2: cur_x -= hw; cur_y -= tl + s + hl + s
                    elif i == 3: cur_x -= hw; cur_y += tl + s
                    elif i == 4: cur_x -= hw; cur_y += tl + s
                else:
                    cur_x += hw
        cur_y = st_y + tl + s + tl + s + s + hl

    return (verts, faces)

#tile hexagonal
def tile_hexagon(ow, ol, tw, s, th):
    verts = []; faces = []; offset = False; z = th
    w = tw / 2; cur_y = 0.0
    h = w * tan(radians(30))
    r = sqrt((w * w) + (h * h))
    while cur_y < ol + tw:
        if offset == False:
            cur_x = 0.0
        else:
            cur_x = w + (s / 2)
        while cur_x < ow:
            p = len(verts)
            v = ((cur_x + w, cur_y + h, 0.0), (cur_x, cur_y + r, 0.0), (cur_x - w, cur_y + h, 0.0), (cur_x - w, cur_y - h, 0.0), (cur_x, cur_y - r, 0.0), (cur_x + w, cur_y - h, 0.0))
            v += ((cur_x + w, cur_y + h, z), (cur_x, cur_y + r, z), (cur_x - w, cur_y + h, z), (cur_x - w, cur_y - h, z), (cur_x, cur_y - r, z), (cur_x + w, cur_y - h, z))
            for vert in v: verts.append(vert)
            n = p
            for i in range(5):
                faces.append((n, n + 1, n + 7, n + 6)); n += 1
            faces.append((p + 5, p, p + 6, p + 11)); f = ((p + 3, p + 2, p + 1, p), (p + 5, p + 4, p + 3, p), (p + 6, p + 7, p + 8, p + 9), (p + 9, p + 10, p + 11, p + 6))
            for face in f: faces.append(face)
            cur_x += tw + s
        cur_y += r + h + s
        if offset == True: offset = False
        elif offset == False: offset = True

    return (verts, faces)

#tile large + many small
def tile_lms(ow, ol, tw, s, th):
    verts = []; faces = []; small = True; z = th
    cur_y = 0.0; ref = (tw - s) / 2
    while cur_y < ol:
        cur_x = 0.0
        large = False
        while cur_x < ow:
            tw2 = ref; tl2 = ref; tw3 = tw; tl3 = tw
            if small == True:
                if cur_x < ow and cur_x + ref > ow:
                    tw2 = ow - cur_x
                if cur_y < ol and cur_y + ref > ol:
                    tl2 = ol - cur_y
                p = len(verts); v = ((cur_x, cur_y + tl2, 0.0), (cur_x, cur_y + tl2, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_x += tw2
                v += ((cur_x, cur_y + tl2, 0.0), (cur_x, cur_y + tl2, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0))
                for vert in v: verts.append(vert)
                f = ((p, p + 3, p + 2, p + 1), (p + 4, p + 5, p + 6, p + 7), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4))
                for face in f: faces.append(face)
            else:
                if large == False:
                    error = []
                    for i in range(2):
                        if cur_x < ow and cur_y < ol:
                            if cur_x + ref > ow: tw2 = ow - cur_x
                            if cur_y + ref > ol: tl2 = ol - cur_y
                            p = len(verts); v = ((cur_x, cur_y + tl2, 0.0), (cur_x, cur_y + tl2, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_x += tw2
                            v += ((cur_x, cur_y + tl2, 0.0), (cur_x, cur_y + tl2, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_y += tl2 + s; cur_x -= tw2; error.append(tl2)
                            for vert in v: verts.append(vert)
                            f = ((p, p + 3, p + 2, p + 1), (p + 4, p + 5, p + 6, p + 7), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4))
                            for face in f: faces.append(face)
                    for i in error:
                        cur_y -= i + s
                    large = True; cur_x += tw2
                elif large == True:
                    if cur_x + tw > ow: tw3 = ow - cur_x
                    if cur_y + tw > ol: tl3 = ol - cur_y
                    p = len(verts); v = ((cur_x, cur_y + tl3, 0.0), (cur_x, cur_y + tl3, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_x += tw3
                    v += ((cur_x, cur_y + tl3, 0.0), (cur_x, cur_y + tl3, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0))
                    for vert in v: verts.append(vert)
                    f = ((p, p + 3, p + 2, p + 1), (p + 4, p + 5, p + 6, p + 7), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4))
                    for face in f: faces.append(face)
                    large = False
            cur_x += s
        if small == True: small = False; cur_y += tl2 + s
        elif small == False: small = True; cur_y += tl3 + s
    return (verts, faces)

#tile regular
def tile_regular(ow, ol, tw, tl, s, is_offset, offset, is_ran_offset, offset_vary, th):
    verts = []; faces = []; off = False; o = 1 / (100 / offset)
    cur_y = 0.0; z = th
    while cur_y < ol:
        cur_x = 0.0; tl2 = tl
        if cur_y < ol and cur_y + tl > ol:
            tl2 = ol - cur_y
        while cur_x < ow:
            p = len(verts); tw2 = tw
            if cur_x < ow and cur_x + tw > ow:
                tw2 = ow - cur_x
            elif cur_x == 0.0 and off == True and is_offset == True and is_ran_offset == False:
                tw2 = tw * o
            elif cur_x == 0.0 and is_offset == True and is_ran_offset == True:
                v = tl * 0.0049 * offset_vary
                tw2 = uniform((tl/ 2) - v, (tl / 2) + v)
            v = ((cur_x, cur_y + tl2, 0.0), (cur_x, cur_y + tl2, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_x += tw2
            v += ((cur_x, cur_y + tl2, 0.0), (cur_x, cur_y + tl2, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_x += s
            for vert in v: verts.append(vert)
            f = ((p, p + 3, p + 2, p + 1), (p + 4, p + 5, p + 6, p + 7), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4))
            for face in f: faces.append(face)
        cur_y += tl2 + s
        if off == False: off = True
        elif off == True: off = False
    return (verts, faces)

#wood parquet
def wood_parquet(ow, ol, bw, s, num_boards, th):
    verts = []; faces = []; cur_x = 0.0; z = th; start_o = "length"
    #figure board length
    bl = (bw * num_boards) + (s * (num_boards - 1))
    while cur_x < ow:
        cur_y = 0.0
        orient = start_o
        while cur_y < ol:
            if orient == "length":
                start_x = cur_x
                for i in range(num_boards):
                    bl2 = bl; bw2 = bw
                    if cur_x < ow and cur_y < ol:
                        #make sure board should be placed
                        if cur_x < ow and cur_x + bw > ow: bw2 = ow - cur_x
                        if cur_y < ol and cur_y + bl > ol: bl2 = ol - cur_y
                        p = len(verts)
                        v = ((cur_x, cur_y, 0.0), (cur_x, cur_y, z), (cur_x + bw2, cur_y, z), (cur_x + bw2, cur_y, 0.0)); cur_y += bl2
                        v += ((cur_x, cur_y, 0.0), (cur_x, cur_y, z), (cur_x + bw2, cur_y, z), (cur_x + bw2, cur_y, 0.0)); cur_y -= bl2; cur_x += bw2 + s
                        for vert in v: verts.append(vert)
                        f = ((p, p + 3, p + 2, p + 1), (p + 4, p + 5, p + 6, p + 7), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4))
                        for face in f: faces.append(face)
                cur_x = start_x; cur_y += bl2 + s
            elif orient == "width":
                for i in range(num_boards):
                    bl2 = bl; bw2 = bw
                    if cur_x < ow and cur_y < ol:
                        if cur_x < ow and cur_x + bl > ow: bl2 = ow - cur_x
                        if cur_y < ol and cur_y + bw > ol: bw2 = ol - cur_y
                        p = len(verts)
                        v = ((cur_x, cur_y + bw2, 0.0), (cur_x, cur_y + bw2, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_x += bl2
                        v += ((cur_x, cur_y + bw2, 0.0), (cur_x, cur_y + bw2, z), (cur_x, cur_y, z), (cur_x, cur_y, 0.0)); cur_x -= bl2; cur_y += bw2 + s
                        for vert in v: verts.append(vert)
                        f = ((p, p + 3, p + 2, p + 1), (p + 4, p + 5, p + 6, p + 7), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4))
                        for face in f: faces.append(face)
            if orient == "length": orient = "width"
            else: orient = "length"
        if start_o == "length": start_o = "width"
        else: start_o = "length"
        cur_x += bl + s

    return (verts, faces)

#wood regular
def wood_regular(ow, ol, bw, bl, s_l, s_w, is_length_vary, length_vary, is_width_vary, width_vary, max_boards, is_r_h, r_h, th):
    verts = []; faces = []; cur_x = 0.0; zt = th
    while cur_x < ow:
        if is_width_vary == True:
            v = bw * width_vary * 0.0048
            bw2 = uniform(bw - v, bw + v)
        else:
            bw2 = bw
        if bw2 + cur_x > ow:
            bw2 = ow - cur_x
        cur_y = 0.0; counter = 1
        while cur_y < ol:
            z = zt
            if is_r_h == True:
                v = z * 0.0045 * r_h
                z = uniform(z - v, z + v)
            bl2 = bl
            if is_length_vary == True:
                v = bl * length_vary * 0.0048
                bl2 = uniform(bl - v, bl + v)
            if (counter >= max_boards and is_length_vary == True) or cur_y + bl2 > ol:
                bl2 = ol - cur_y
            p = len(verts)
            #verts
            v = ((cur_x, cur_y, 0.0), (cur_x, cur_y, z), (cur_x + bw2, cur_y, z), (cur_x + bw2, cur_y, 0.0)); cur_y += bl2
            v += ((cur_x, cur_y, 0.0), (cur_x, cur_y, z), (cur_x + bw2, cur_y, z), (cur_x + bw2, cur_y, 0.0)); cur_y += s_l
            for vert in v: verts.append(vert)
            #faces
            f = ((p, p + 3, p + 2, p + 1), (p, p + 4, p + 7, p + 3), (p + 3, p + 7, p + 6, p + 2), (p + 1, p + 2, p + 6, p + 5), (p, p + 1, p + 5, p + 4), (p + 4, p + 5, p + 6, p + 7))
            for face in f: faces.append(face)
            counter += 1
        cur_x += bw2 + s_w
    return (verts, faces)

def tile_grout(ow, ol, depth, th):
    verts = []; faces = []; z = th - depth
    x = ow; y = ol
    v = ((0.0, 0.0, 0.0), (0.0, 0.0, z), (x, 0.0, z), (x, 0.0, 0.0), (0.0, y, 0.0), (0.0, y, z), (x, y, z), (x, y, 0.0))
    for vert in v: verts.append(vert)
    f = ((0, 3, 2, 1), (4, 5, 6, 7), (0, 1, 5, 4), (1, 2, 6, 5), (3, 7, 6, 2), (0, 4, 7, 3))
    for face in f: faces.append(face)
    return (verts, faces)

def tile_cutter(ow, ol):
    verts = []; faces = []; z = 0.5
    x = ow; y = ol
    v = ((0.0, 0.0, -0.5), (0.0, 0.0, z), (x, 0.0, z), (x, 0.0, -0.5), (0.0, y, -0.5), (0.0, y, z), (x, y, z), (x, y, -0.5))
    for vert in v: verts.append(vert)
    f = ((0, 3, 2, 1), (4, 5, 6, 7), (0, 1, 5, 4), (1, 2, 6, 5), (3, 7, 6, 2), (0, 4, 7, 3))
    for face in f: faces.append(face)
    return (verts, faces)

def UpdateFlooring(self, context):
    o = context.object; mats = []
    for i in o.data.materials:
        mats.append(i.name)
    pre_scale = tuple(o.scale.copy())
    if tuple(o.scale.copy()) != (1.0, 1.0, 1.0) and o.f_object_add == "convert":
        bpy.ops.object.transform_apply(scale = True)
    dim = []
    for i in tuple(o.dimensions.copy()):
        i += 0.1; i *= 3.28084; dim.append(i)

    #generate faces and vertices
    if o.f_object_add == "add":
        verts, faces = create_flooring(o.f_mat, o.f_if_wood, o.f_if_tile, o.f_over_width, o.f_over_length, o.f_b_width, o.f_b_length, o.f_b_length2, o.f_is_length_vary, o.f_length_vary, o.f_num_boards,
                o.f_space_l, o.f_space_w, o.f_spacing, o.f_t_width, o.f_t_length, o.f_is_offset, o.f_offset, o.f_is_random_offset,
                o.f_offset_vary, o.f_t_width2, o.f_is_width_vary, o.f_width_vary, o.f_max_boards, o.f_is_ran_height, o.f_ran_height, o.f_thickness, o.f_hb_direction)
    elif o.f_object_add == "convert":
        verts, faces = create_flooring(o.f_mat, o.f_if_wood, o.f_if_tile, dim[0], dim[1], o.f_b_width, o.f_b_length, o.f_b_length2, o.f_is_length_vary, o.f_length_vary, o.f_num_boards,
                o.f_space_l, o.f_space_w, o.f_spacing, o.f_t_width, o.f_t_length, o.f_is_offset, o.f_offset, o.f_is_random_offset,
                o.f_offset_vary, o.f_t_width2, o.f_is_width_vary, o.f_width_vary, o.f_max_boards, o.f_is_ran_height, o.f_ran_height, o.f_thickness, o.f_hb_direction)

    if o.f_object_add in ("convert", "add"):
        emesh = o.data
        mesh = bpy.data.meshes.new(name = "flooring")
        mesh.from_pydata(verts, [], faces)
        mesh.update(calc_edges = True)

    if o.f_object_add == "convert":
        if o.f_cut_name == "none": #create cutter object
            for ob in context.scene.objects:
                ob.select = False
            cutter = bpy.data.objects.new(o.name + "_cutter", o.data.copy())
            context.scene.objects.link(cutter); cutter.location = o.location.copy(); cutter.rotation_euler = o.rotation_euler.copy(); cutter.scale = o.scale.copy()
            o.f_cut_name = cutter.name; cutter.select = True; context.scene.objects.active = cutter
            bpy.ops.object.modifier_add(type = "SOLIDIFY"); pos = len(cutter.modifiers) - 1
            cutter.modifiers[pos].offset = 0; cutter.modifiers[pos].thickness = 1
            o.select = True; context.scene.objects.active = o
            #getting extreme position
            verts = [vert.co for vert in o.data.vertices] #get vertex data
            tup_verts = [vert.to_tuple() for vert in verts] #convert to tuples
            x = None; z = None; y = None; coords = []
            for i in tup_verts: #find smallest x and z values
                if x == None: x = i[0]
                elif i[0] < x: x = i[0]
                if z == None: z = i[2]
                elif i[2] < z: z = i[2]
                if y == None: y = i[1]
                elif i[1] < y: y = i[1]
            position = o.matrix_world * Vector((x, y, z)) #get world space
            coords = position
        elif o.f_cut_name in bpy.data.objects:
            (bpy.data.objects[o.f_cut_name]).name = o.name + "_cutter"; o.f_cut_name = o.name + "_cutter"; coords = o.location.copy()
        else: self.report({"ERROR"}, "Can't Find Cutter Object"); bpy.ops.object.delete()
    for i in bpy.data.objects:
        if i.data == emesh:
            i.data = mesh
    emesh.user_clear(); bpy.data.meshes.remove(emesh)

    #modifiers
    if o.f_is_bevel == True and o.f_mat == "1":
        bpy.ops.object.modifier_add(type = "BEVEL"); pos = len(o.modifiers) - 1
        o.modifiers[pos].segments = o.f_res
        o.modifiers[pos].width = o.f_bevel_amo
        bpy.ops.object.modifier_apply(apply_as = "DATA", modifier = o.modifiers[pos].name)

    #set position
    if o.f_object_add == "convert":
        o.location = coords; cutter = bpy.data.objects[o.f_cut_name]
        for ob in bpy.data.objects:
            ob.select = False
        cursor = context.scene.cursor_location.copy(); o.select = True; bpy.context.scene.objects.active = o
        bpy.ops.view3d.snap_cursor_to_selected(); o.select = False
        if o.f_is_cut == "none":
            cutter.select = True; bpy.context.scene.objects.active = cutter
            bpy.ops.object.origin_set(type = "ORIGIN_CURSOR")
            bpy.ops.object.move_to_layer(layers = (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True))
            cutter.select = False; o.f_is_cut = "cut"
        o.select = True; bpy.context.scene.objects.active = o
        cutter.location = o.location.copy(); cutter.rotation_euler = o.rotation_euler.copy()
        if pre_scale != (1.0, 1.0, 1.0):
            layers = [i for i in bpy.context.scene.layers]; counter = 1; true = []
            for i in layers:
                if i == True: true.append(counter)
                counter += 1
            for area in bpy.context.screen.areas:
                if area.type == 'VIEW_3D':
                    override = bpy.context.copy()
                    override['area'] = area
                    bpy.ops.view3d.layers(override, nr = 20, toggle = True)
            cutter.select = True; o.select = False; bpy.context.scene.objects.active = cutter
            cutter.scale = (pre_scale[0], pre_scale[2], pre_scale[1]); bpy.ops.object.transform_apply(scale = True)
            cutter.select = False
            for area in bpy.context.screen.areas:
                if area.type == 'VIEW_3D':
                    override = bpy.context.copy()
                    override['area'] = area
                    for i in true:
                        bpy.ops.view3d.layers(override, nr = i, extend = True, toggle = True)
                    bpy.ops.view3d.layers(override, nr = 20, extend = True, toggle = True)
            bpy.context.scene.objects.active = o; o.select = True
        bpy.context.scene.cursor_location = cursor #change cursor location back to original
        #cut
        bpy.ops.object.modifier_add(type = "BOOLEAN"); pos = len(o.modifiers) - 1
        bpy.context.object.modifiers[pos].object = bpy.data.objects[o.f_cut_name]
        bpy.ops.object.modifier_apply(apply_as = "DATA", modifier = o.modifiers[0].name)

    #create grout
    if o.f_mat == "2":
        if o.f_object_add == "add":
            verts2, faces2 = tile_grout(o.f_over_width, o.f_over_length, o.f_grout_depth, o.f_thickness)
        else:
            c = bpy.data.objects[o.f_cut_name]
            verts2, faces2 = tile_grout(c.dimensions[0], c.dimensions[1], o.f_grout_depth, o.f_thickness)
        grout = bpy.data.meshes.new(name = "grout")
        grout.from_pydata(verts2, [], faces2)
        grout_ob = bpy.data.objects.new("grout", grout)
        context.scene.objects.link(grout_ob); grout_ob.rotation_euler = o.rotation_euler; grout_ob.location = o.location
        if o.f_object_add == "convert":
            o.select = False; grout_ob.select = True; context.scene.objects.active = grout_ob
            bpy.ops.object.modifier_add(type = "BOOLEAN"); pos = len(grout_ob.modifiers) - 1
            bpy.context.object.modifiers[pos].object = bpy.data.objects[o.f_cut_name]
            bpy.ops.object.modifier_apply(apply_as = "DATA", modifier = grout_ob.modifiers[0].name)
            grout_ob.select = False; o.select = True; context.scene.objects.active = o

    #cut tile or herringbone wood
    if o.f_object_add == "add" and (o.f_mat == "2" and o.f_if_tile in ("2", "4")) or (o.f_mat == "1" and o.f_if_wood in ("3", "4")):
        verts3, faces3 = tile_cutter(o.f_over_width, o.f_over_length)
        cutter_me = bpy.data.meshes.new("cutter")
        cutter_me.from_pydata(verts3, [], faces3)
        cutter_ob = bpy.data.objects.new("cutter", cutter_me)
        context.scene.objects.link(cutter_ob); cutter_ob.location = o.location; cutter_ob.rotation_euler = o.rotation_euler; cutter_ob.scale = o.scale
        for ob in bpy.data.objects:
            ob.select = False
        o.select = True; context.scene.objects.active = o
        bpy.ops.object.modifier_add(type = "BOOLEAN"); pos = len(o.modifiers) - 1
        o.modifiers[pos].object = cutter_ob
        bpy.ops.object.modifier_apply(apply_as = "DATA", modifier = o.modifiers[pos].name)
        o.select = False

        #cut grout if needed
        if o.f_mat == "2":
            grout_ob.select = True; context.scene.objects.active = grout_ob
            bpy.ops.object.modifier_add(type = "BOOLEAN"); pos = len(grout_ob.modifiers) - 1
            grout_ob.modifiers[pos].object = cutter_ob
            bpy.ops.object.modifier_apply(apply_as = "DATA", modifier = grout_ob.modifiers[pos].name)
            grout_ob.select = False

        cutter_ob.select = True; context.scene.objects.active = cutter_ob; bpy.ops.object.delete()
        o.select = True; context.scene.objects.active = o

    #add materials
    if o.f_mat == "2": #grout material
        enter = True
        for i in mats:
            if "grout_" in i or len(mats) >= 2: enter = False
        if enter == True:
            mat = bpy.data.materials.new("grout_temp"); mat.use_nodes = True; grout_ob.data.materials.append(mat)
        elif enter == False:
            mat = bpy.data.materials.get(mats[1]); grout_ob.data.materials.append(mat)

    if o.f_is_material == True or o.f_mat == "2":
        enter = True
        for i in mats:
            if "flooring_" in i: enter = False
        if enter == True:
            mat = bpy.data.materials.new("flooring_" + o.name); mat.use_nodes = True; o.data.materials.append(mat)
        elif enter == False:
            mat = bpy.data.materials.get(mats[0]); o.data.materials.append(mat)

    #join grout
    if o.f_mat == "2":
        VertexGroup(self, context)
        for ob in bpy.data.objects:
            ob.select = False
        grout_ob.select = True; o.select = True; context.scene.objects.active = o; bpy.ops.object.join()
    for i in mats:
        if i not in o.data.materials:
            mat = bpy.data.materials.get(i); o.data.materials.append(mat)

    #uv unwrap
    if o.f_unwrap == True:
        UnwrapFlooring(self, context)
        if o.f_random_uv == True:
            RandomUV(self, context)

def UnwrapFlooring(self, context):
    o = context.object
    #uv unwrap
    for i in bpy.data.objects: i.select = False
    o.select = True; bpy.context.scene.objects.active = o
    for area in bpy.context.screen.areas:
        if area.type == 'VIEW_3D':
            for region in area.regions:
                if region.type == 'WINDOW':
                    bpy.ops.object.editmode_toggle()
                    override = bpy.context.copy(); override["area"] = area; override["region"] = region; override["active_object"] = (bpy.context.selected_objects)[0]
                    bpy.ops.mesh.select_all(action = "SELECT"); bpy.ops.uv.cube_project(override); bpy.ops.object.editmode_toggle()

def RandomUV(self, context):
    for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                for region in area.regions:
                    if region.type == 'WINDOW':
                        bpy.ops.object.editmode_toggle()
                        bpy.ops.mesh.select_all(action = "SELECT")
                        obj = bpy.context.object
                        me = obj.data
                        bm = bmesh.from_edit_mesh(me)

                        uv_layer = bm.loops.layers.uv.verify()
                        bm.faces.layers.tex.verify()
                        # adjust UVs
                        for f in bm.faces:
                            offset = Vector((uniform(-1.0, 1.0), uniform(-1.0, 1.0)))
                            for v in f.loops:
                                luv = v[uv_layer]
                                luv.uv = (luv.uv + offset).xy

                        bmesh.update_edit_mesh(me)
                        bpy.ops.object.editmode_toggle()

def DeleteMaterials(self, context):
    o = context.object
    if o.f_is_material == False and o.f_mat != "2":
        for i in o.data.materials:
            bpy.ops.object.material_slot_remove()
        for i in bpy.data.materials:
            if i.users == 0:
                bpy.data.materials.remove(i)

def PreviewMaterials(self, context):
    for area in bpy.context.screen.areas:
        if area.type == 'VIEW_3D':
            for space in area.spaces:
                if space.type == 'VIEW_3D':
                    if bpy.context.object.f_is_preview == True: space.viewport_shade = 'RENDERED'
                    else: space.viewport_shade = "SOLID"

def VertexGroup(self, context):
    o = context.object
    for i in bpy.data.objects: i.select = False
    o.select = True; bpy.context.scene.objects.active = o
    for area in bpy.context.screen.areas:
        if area.type == 'VIEW_3D':
            for region in area.regions:
                if region.type == 'WINDOW':
                    bpy.ops.object.editmode_toggle()
                    if "JARCH" in o.vertex_groups:
                        group = o.vertex_groups.get("JARCH"); o.vertex_groups.remove(group)
                    bpy.ops.object.vertex_group_add(); bpy.ops.object.vertex_group_assign(); active = o.vertex_groups.active
                    active.name = "JARCH"; bpy.ops.object.editmode_toggle()

def FlooringMaterial(self, context):
    o = bpy.context.object; error = False
    if o.f_col_image == "": self.report({"ERROR"}, "No Color Image Entered"); error = True
    if o.f_is_bump == True and o.f_norm_image == "": self.report({"ERROR"}, "No Normal Map Image Entered"); error = True
    if error == False:
        extra_rot = None
        if o.f_mat == "1" and o.f_if_wood in ("3", "4"):
            extra_rot = 45

        mat = Image(bpy, context, o.f_im_scale, o.f_col_image, o.f_norm_image, o.f_bump_amo, o.f_is_bump, "flooring_use", True, 0.1, 0.05, o.f_is_rotate, extra_rot)
        if mat != None:
            if len(o.data.materials) == 0:
                o.data.materials.append(mat.copy())
            else:
                o.data.materials[0] = mat.copy()
            o.data.materials[0].name = "flooring_" + o.name
        else: self.report({"ERROR"}, "Images Not Found, Make Sure Path Is Correct")
    if error == False and o.f_mat == "2" and len(o.data.materials) >= 2:
        mat2 = Mortar(bpy, context, o.f_mortar_color, o.f_mortar_bump, o.data.materials[1].name)
        o.data.materials[1] = mat2.copy(); o.data.materials[1].name = "mortar_" + o.name
    #remove extra materials
    for i in bpy.data.materials:
        if i.users == 0: bpy.data.materials.remove(i)

#properties
#setup
bpy.types.Object.f_object_add = StringProperty(default = "none", update = UpdateFlooring)
bpy.types.Object.f_cut_name = StringProperty(default = "none")
bpy.types.Object.f_is_cut = StringProperty(default = "none")
#type/material
bpy.types.Object.f_mat = EnumProperty(items = (("1", "Wood", ""), ("2", "Tile", "")), default = "1", description = "Material", update = UpdateFlooring, name = "")
bpy.types.Object.f_if_wood = EnumProperty(items = (("1", "Regular", ""), ("2", "Parquet", ""), ("3", "Herringbone Parquet", ""), ("4", "Herringbone", "")), default = "1", description = "Wood Type", update = UpdateFlooring, name = "")
bpy.types.Object.f_if_tile = EnumProperty(items = (("1", "Regular", ""), ("2", "Large + Small", ""), ("3", "Large + Many Small", ""), ("4", "Hexagonal", "")), default = "1", description = "Tile Type", update = UpdateFlooring, name = "")
#measurements
bpy.types.Object.f_over_width = FloatProperty(name = "Overall Width", min = 2.00 / 3.28084, max = 100.00 / 3.28084, default = 8.00 / 3.28084, subtype = "DISTANCE", description = "Overall Width", update = UpdateFlooring)
bpy.types.Object.f_over_length = FloatProperty(name = "Overall Length", min = 2.0 / 3.28084, max = 100.00 / 3.28084, default = 8.00 / 3.28084, subtype = "DISTANCE", description = "Overall Length", update = UpdateFlooring)
bpy.types.Object.f_b_width = FloatProperty(name = "Board Width", min = 2.00 / 39.3701, max = 14.00 / 39.3701, default = 6.00 / 39.3701, subtype = "DISTANCE", description = "Board Width", update = UpdateFlooring)
bpy.types.Object.f_b_length = FloatProperty(name = "Board Length", min = 4.00 / 3.28084, max = 20.00 / 3.28084, default = 8.00 / 3.28084, subtype = "DISTANCE", description = "Board Length", update = UpdateFlooring)
bpy.types.Object.f_b_length2 = FloatProperty(name = "Board Length", min = 1.00 / 3.28084, max = 4.00 / 3.28084, default = 1.5 / 3.28084, subtype = "DISTANCE", description = "Board Length", update = UpdateFlooring)
bpy.types.Object.f_hb_direction = EnumProperty(items = (("1", "Forwards (+y)", ""), ("2", "Backwards (-y)", ""), ("3", "Right (+x)", ""), ("4", "Left (-x)", "")), name = "Direction", description = "Herringbone Direction", update = UpdateFlooring)
bpy.types.Object.f_thickness = FloatProperty(name = "Floor Thickness", min = 0.75 / 39.3701, max = 1.5 / 39.3701, default = 1 / 39.3701, subtype = "DISTANCE", description = "Thickness Of Flooring", update = UpdateFlooring)
bpy.types.Object.f_is_length_vary = BoolProperty(name = "Vary Length?", default = False, description = "Vary Lengths?", update = UpdateFlooring)
bpy.types.Object.f_length_vary = FloatProperty(name = "Length Varience", min = 1.00, max = 100.0, default = 50.0, subtype = "PERCENTAGE", description = "Length Varience", update = UpdateFlooring)
bpy.types.Object.f_max_boards = IntProperty(name = "Max # Of Boards", min = 2, max = 10, default = 2, description = "Maximum Number Of Boards Possible In One Length", update = UpdateFlooring)
bpy.types.Object.f_is_width_vary = BoolProperty(name = "Vary Width?", default = False, description = "Vary Widths?", update = UpdateFlooring)
bpy.types.Object.f_width_vary = FloatProperty(name = "Width Varience", min = 1.00, max = 100.0, default = 50.0, subtype = "PERCENTAGE", description = "Width Varience", update = UpdateFlooring)
bpy.types.Object.f_num_boards = IntProperty(name = "# Of Boards", min = 2, max = 6, default = 4, description = "Number Of Boards In Square", update = UpdateFlooring)
bpy.types.Object.f_space_l = FloatProperty(name = "Length Spacing", min = 0.001 / 39.3701, max = 0.5 / 39.3701, default = 0.125 / 39.3701, subtype = "DISTANCE", description = "Space Between Boards Length Ways", update = UpdateFlooring)
bpy.types.Object.f_space_w = FloatProperty(name = "Width Spacing", min = 0.001 / 39.3701, max = 0.5 / 39.3701, default = 0.125 / 39.3701, subtype = "DISTANCE", description = "Space Between Boards Width Ways", update = UpdateFlooring)
bpy.types.Object.f_spacing = FloatProperty(name = "Spacing", min = 0.001 / 39.3701, max = 1.0 / 39.3701, default = 0.25 / 39.3701, subtype = "DISTANCE", description = "Space Between Tiles", update = UpdateFlooring)
bpy.types.Object.f_is_bevel = BoolProperty(name = "Bevel?", default = False, update = UpdateFlooring)
bpy.types.Object.f_res = IntProperty(name = "Bevel Resolution", min = 1, max = 5, default = 1, update = UpdateFlooring)
bpy.types.Object.f_bevel_amo = FloatProperty(name = "Bevel Amount", min = 0.001 / 39.3701, max = 0.5 / 39.3701, default = 0.15 / 39.3701, subtype = "DISTANCE", description = "Bevel Amount", update = UpdateFlooring)
bpy.types.Object.f_is_ran_height = BoolProperty(name = "Random Heights?", default = False, update = UpdateFlooring)
bpy.types.Object.f_ran_height = FloatProperty(name = "Height Varience", min = 0.1, max = 100.0, default = 50.0, subtype = "PERCENTAGE", update = UpdateFlooring)
#tile specific
bpy.types.Object.f_t_width = FloatProperty(name = "Tile Width", min = 2.00 / 39.3701, max = 24.00 / 39.3701, default = 8.00 / 39.3701, subtype = "DISTANCE", description = "Tile Width", update = UpdateFlooring)
bpy.types.Object.f_t_length = FloatProperty(name = "Tile Length", min = 2.00 / 39.3701, max = 24.00 / 39.3701, default = 8.00 / 39.3701, subtype = "DISTANCE", description = "Tile Length", update = UpdateFlooring)
bpy.types.Object.f_grout_depth = FloatProperty(name = "Grout Depth", min = 0.01 / 39.3701, max = 0.40 / 39.701, default = 0.10 / 39.3701, subtype = "DISTANCE", description = "Grout Depth", update = UpdateFlooring)
bpy.types.Object.f_is_offset = BoolProperty(name = "Offset Tiles?", default = False, description = "Offset Tile Rows", update = UpdateFlooring)
bpy.types.Object.f_offset = FloatProperty(name = "Offset", min = 0.001, max = 100.0, default = 50.0, subtype = "PERCENTAGE", description = "Tile Offset Amount", update = UpdateFlooring)
bpy.types.Object.f_is_random_offset = BoolProperty(name = "Random Offset?", default = False, description = "Offset Tile Rows Randomly", update = UpdateFlooring)
bpy.types.Object.f_offset_vary = FloatProperty(name = "Offset Varience", min = 0.001, max = 100.0, default = 50.0, subtype = "PERCENTAGE", description = "Offset Varience", update = UpdateFlooring)
bpy.types.Object.f_t_width2 = FloatProperty(name = "Small Tile Width", min = 2.00 / 39.3701, max = 10.00 / 39.3701, default = 6.00 / 39.3701, subtype = "DISTANCE", description = "Small Tile Width", update = UpdateFlooring)
#materials
bpy.types.Object.f_is_material = BoolProperty(name = "Cycles Materials?", default = False, description = "Adds Cycles Materials", update = DeleteMaterials)
bpy.types.Object.f_is_preview = BoolProperty(name = "Preview Material?", default = False, description = "Preview Material On Object", update = PreviewMaterials)
bpy.types.Object.f_im_scale = FloatProperty(name = "Image Scale", max = 10.0, min = 0.1, default = 1.0, description = "Change Image Scaling")
bpy.types.Object.f_col_image = StringProperty(name = "", subtype = "FILE_PATH", description = "File Path For Color Image")
bpy.types.Object.f_is_bump = BoolProperty(name = "Normal Map?", default = False, description = "Add Normal To Material?")
bpy.types.Object.f_norm_image = StringProperty(name = "", subtype = "FILE_PATH", description = "File Path For Normal Map Image")
bpy.types.Object.f_bump_amo = FloatProperty(name = "Normal Stength", min = 0.001, max = 2.000, default = 0.250, description = "Normal Map Strength")
bpy.types.Object.f_unwrap = BoolProperty(name = "UV Unwrap?", default = True, description = "UV Unwraps Siding", update = UnwrapFlooring)
bpy.types.Object.f_mortar_color = FloatVectorProperty(name = "Mortar Color", subtype = "COLOR", default = (1.0, 1.0, 1.0), min = 0.0, max = 1.0, description = "Color For Mortar")
bpy.types.Object.f_mortar_bump = FloatProperty(name = "Mortar Bump", min = 0.0, max = 1.0, default = 0.25, description = "Mortar Bump Amount")
bpy.types.Object.f_is_rotate = BoolProperty(name = "Rotate Image?", default = False, description = "Rotate Image 90 Degrees")
bpy.types.Object.f_random_uv = BoolProperty(name = "Random UV's?", default = True, description = "Random UV's", update = UpdateFlooring)

class FlooringMaterials(bpy.types.Operator):
    bl_idname = "mesh.jarch_flooring_materials"
    bl_label = "Generate\\Update Materials"
    bl_options = {"UNDO", "INTERNAL"}

    def execute(self, context):
        FlooringMaterial(self, context)
        return {"FINISHED", "INTERNAL"}

class FlooringPanel(bpy.types.Panel):
    bl_idname = "OBJECT_PT_jarch_flooring"
    bl_label = "JARCH Vis: Flooring"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_category = "JARCH Vis"
    bl_options = {"DEFAULT_CLOSED"}

    def draw(self, context):
        layout = self.layout
        if bpy.context.mode == "EDIT_MESH":
            layout.label("JARCH Vis Doesn't Work In Edit Mode", icon = "ERROR")
        else:
            o = context.object
            if o != None:
                if o.type == "MESH":
                    if o.object_add == "none" and o.s_object_add == "none" and o.ro_object_add == "none":
                        if o.f_object_add in ("convert", "add"):
                            layout.label("Material:"); layout.prop(o, "f_mat", icon = "MATERIAL"); layout.label("Types:")
                            if o.f_mat == "1": layout.prop(o, "f_if_wood", icon = "OBJECT_DATA")
                            elif o.f_mat == "2": layout.prop(o, "f_if_tile", icon = "OBJECT_DATA")
                            layout.separator()
                            if o.f_object_add == "add":
                                layout.prop(o, "f_over_width"); layout.prop(o, "f_over_length"); layout.separator()
                            #width and lengths
                            layout.prop(o, "f_thickness"); layout.separator()

                            if o.f_mat == "1":
                                layout.prop(o, "f_b_width")

                                if o.f_if_wood == "1": layout.prop(o, "f_b_length")
                                layout.separator()

                                if o.f_if_wood == "1":
                                    layout.prop(o, "f_is_length_vary", icon = "NLA")
                                    if o.f_is_length_vary == True: layout.prop(o, "f_length_vary"); layout.prop(o, "f_max_boards"); layout.separator()
                                    layout.prop(o, "f_is_width_vary", icon = "UV_ISLANDSEL")
                                    if o.f_is_width_vary == True:
                                        layout.prop(o, "f_width_vary"); layout.separator()
                                    layout.prop(o, "f_is_ran_height", icon = "RNDCURVE")
                                    if o.f_is_ran_height == True: layout.prop(o, "f_ran_height")
                                    layout.separator()
                                    layout.prop(o, "f_space_w"); layout.prop(o, "f_space_l"); layout.separator()

                                elif o.f_if_wood in ("3", "4"):
                                    layout.prop(o, "f_b_length2")
                                    layout.prop(o, "f_hb_direction")
                                    layout.separator()

                                if o.f_if_wood != "1":
                                    layout.prop(o, "f_spacing")
                                    layout.separator()
                                if o.f_if_wood == "2":
                                    layout.prop(o, "f_num_boards")
                                    layout.separator()

                                #bevel
                                layout.prop(o, "f_is_bevel", icon = "MOD_BEVEL")
                                if o.f_is_bevel == True: layout.prop(o, "f_res", icon = "OUTLINER_DATA_CURVE"); layout.prop(o, "f_bevel_amo"); layout.separator()

                            elif o.f_mat == "2":
                                if o.f_if_tile != "4": layout.prop(o, "f_t_width"); layout.prop(o, "f_t_length"); layout.separator()
                                else:
                                    layout.prop(o, "f_t_width2"); layout.separator()
                                if o.f_if_tile == "1":
                                    layout.prop(o, "f_is_offset", icon = "OOPS")
                                    if o.f_is_offset == True:
                                        layout.prop(o, "f_is_random_offset", icon = "NLA")
                                        if o.f_is_random_offset == False: layout.prop(o, "f_offset")
                                        else: layout.prop(o, "f_offset_vary")
                                    layout.separator()
                                layout.prop(o, "f_grout_depth"); layout.prop(o, "f_spacing"); layout.separator()
                            layout.prop(o, "f_unwrap", icon = "GROUP_UVS")

                            if o.f_unwrap == True:
                                layout.prop(o, "f_random_uv", icon = "RNDCURVE")
                            layout.separator()

                            if context.scene.render.engine == "CYCLES": layout.prop(o, "f_is_material", icon = "MATERIAL")
                            else: layout.label("Materials Only Supported With Cycles", icon = "POTATO")

                            if o.f_is_material == True and context.scene.render.engine == "CYCLES":
                                layout.separator(); layout.prop(o, "f_col_image", icon = "COLOR"); layout.prop(o, "f_is_bump", icon = "SMOOTHCURVE")
                                if o.f_is_bump == True: layout.prop(o, "f_norm_image", icon = "TEXTURE"); layout.prop(o, "f_bump_amo")
                                layout.prop(o, "f_im_scale", icon = "MAN_SCALE"); layout.prop(o, "f_is_rotate", icon = "MAN_ROT")
                                if o.f_mat == "2":
                                    layout.separator(); layout.prop(o, "f_mortar_color", icon = "COLOR"); layout.prop(o, "f_mortar_bump")
                                layout.separator(); layout.operator("mesh.jarch_flooring_materials", icon = "MATERIAL")
                                layout.separator(); layout.prop(o, "f_is_preview", icon = "SCENE")
                            layout.separator(); layout.operator("mesh.jarch_flooring_update", icon = "FILE_REFRESH")
                            layout.operator("mesh.jarch_flooring_mesh", icon = "OUTLINER_OB_MESH")
                            layout.operator("mesh.jarch_flooring_delete", icon = "CANCEL")
                        else:
                            if o.f_object_add == "none" and o.s_object_add == "none" and o.ro_object_add == "none" and o.f_object_add != "mesh":
                                layout.operator("mesh.jarch_flooring_convert")
                            elif o.f_object_add == "mesh":
                                layout.label("This Is A Mesh JARCH Vis Object", icon = "INFO")
                    else:
                        layout.label("This Is Already A JARCH Vis Object", icon = "POTATO")
                else:
                    layout.label("Only Mesh Objects Can Be Used", icon = "ERROR")
            else:
                layout.operator("mesh.jarch_flooring_add", text = "Add Flooring", icon = "MESH_GRID")

class FlooringAdd(bpy.types.Operator):
    bl_idname = "mesh.jarch_flooring_add"
    bl_label = "JARCH Vis: Add Flooring"
    bl_description = "JARCH Vis: Flooring Generator"
    @classmethod
    def poll(self, context):
        return context.mode == "OBJECT"
    def execute(self, context):
        bpy.ops.mesh.primitive_cube_add()
        o = bpy.context.scene.objects.active
        o.f_object_add = "add"
        return {"FINISHED"}

class FlooringConvert(bpy.types.Operator):
    bl_idname = "mesh.jarch_flooring_convert"
    bl_label = "Convert To Flooring"
    bl_options = {"UNDO", "INTERNAL"}

    def execute(self, context):
        o = context.object
        o.f_object_add = "convert"
        return {"FINISHED"}

class FlooringUpdate(bpy.types.Operator):
    bl_idname = "mesh.jarch_flooring_update"
    bl_label = "Update Flooring"
    bl_options = {"UNDO", "INTERNAL"}

    def execute(self, context):
        UpdateFlooring(self, context)
        return {"FINISHED"}

class FlooringMesh(bpy.types.Operator):
    bl_idname = "mesh.jarch_flooring_mesh"
    bl_label = "Convert To Mesh"
    bl_description = "Converts Flooring Object To Normal Object (No Longer Editable)"
    bl_options = {"UNDO", "INTERNAL"}

    def execute(self, context):
        o = context.object
        o.f_object_add = "mesh"
        return {"FINISHED"}

class FlooringDelete(bpy.types.Operator):
    bl_idname = "mesh.jarch_flooring_delete"
    bl_label = "Delete Flooring"
    bl_options = {"UNDO", "INTERNAL"}

    def execute(self, context):
        o = context.object; convert = False
        if o.f_object_add == "convert" and o.f_cut_name in bpy.data.objects:
            cutter = bpy.data.objects[o.f_cut_name]; o.select = False
            layers = [i for i in bpy.context.scene.layers]; counter = 1; true = []
            for i in layers:
                if i == True: true.append(counter)
                counter += 1
            o_layers = []
            for i in bpy.context.object.layers: o_layers.append(i)
            for area in bpy.context.screen.areas:
                if area.type == 'VIEW_3D':
                    override = bpy.context.copy()
                    override['area'] = area
                    bpy.ops.view3d.layers(override, nr = 20, toggle = True)
            cutter.select = True; bpy.context.scene.objects.active = cutter; bpy.ops.object.modifier_remove(modifier = "Solidify")
            bpy.ops.object.move_to_layer(layers = o_layers); convert = True; cutter.select = False
            for area in bpy.context.screen.areas:
                if area.type == 'VIEW_3D':
                    override = bpy.context.copy()
                    override['area'] = area
                    for i in true:
                        bpy.ops.view3d.layers(override, nr = i, extend = True, toggle = True)
                    bpy.ops.view3d.layers(override, nr = 20, extend = True, toggle = True)
        m_name = o.name; c_name = o.f_cut_name
        o.select = True; bpy.context.scene.objects.active = o; bpy.ops.object.delete()
        for i in bpy.data.materials:
            if i.users == 0: bpy.data.materials.remove(i)
        if convert == True:
            bpy.data.objects[c_name].name = m_name
        return {"FINISHED"}


# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Logic for clustering similar symbols."""

import collections
import logging
import re

# Refer to models.SymbolGroup.Cluster() for pydoc
def ClusterSymbols(symbols):
  # http://unix.stackexchange.com/questions/223013/function-symbol-gets-part-suffix-after-compilation
  # Example name suffixes:
  #     [clone .part.322]  # GCC
  #     [clone .isra.322]  # GCC
  #     [clone .constprop.1064]  # GCC
  #     [clone .11064]  # clang

  # Step 1: Create name map, find clones, collect star syms into replacements.
  logging.debug('Creating name -> symbol map')
  clone_indices = []
  indices_by_full_name = {}
  # (section_name, name, full_name) -> [(index, sym),...]
  replacements_by_tup = collections.defaultdict(list)
  for i, symbol in enumerate(symbols):
    if symbol.name.startswith('**'):
      # "symbol gap 3" -> "symbol gaps"
      name = re.sub(r'\s+\d+( \(.*\))?$', 's', symbol.name)
      replacements_by_tup[(symbol.section_name, name, None)].append((i, symbol))
    elif symbol.full_name:
      if symbol.full_name.endswith(']') and ' [clone ' in symbol.full_name:
        clone_indices.append(i)
      else:
        indices_by_full_name[symbol.full_name] = i

  # Step 2: Collect same-named clone symbols.
  logging.debug('Grouping all clones')
  group_names_by_index = {}
  for i in clone_indices:
    symbol = symbols[i]
    # Multiple attributes could exist, so search from left-to-right.
    stripped_name = symbol.name[:symbol.name.index(' [clone ')]
    stripped_full_name = symbol.full_name[:symbol.full_name.index(' [clone ')]
    name_tup = (symbol.section_name, stripped_name, stripped_full_name)
    replacement_list = replacements_by_tup[name_tup]

    if not replacement_list:
      # First occurance, check for non-clone symbol.
      non_clone_idx = indices_by_full_name.get(stripped_name)
      if non_clone_idx is not None:
        non_clone_symbol = symbols[non_clone_idx]
        replacement_list.append((non_clone_idx, non_clone_symbol))
        group_names_by_index[non_clone_idx] = stripped_name

    replacement_list.append((i, symbol))
    group_names_by_index[i] = stripped_name

  # Step 3: Undo clustering when length=1.
  # Removing these groups means Diff() logic must know about [clone] suffix.
  to_clear = []
  for name_tup, replacement_list in replacements_by_tup.iteritems():
    if len(replacement_list) == 1:
      to_clear.append(name_tup)
  for name_tup in to_clear:
    del replacements_by_tup[name_tup]

  # Step 4: Replace first symbol from each cluster with a SymbolGroup.
  before_symbol_count = sum(len(x) for x in replacements_by_tup.itervalues())
  logging.debug('Creating %d symbol groups from %d symbols. %d clones had only '
                'one symbol.', len(replacements_by_tup), before_symbol_count,
                len(to_clear))

  len_delta = len(replacements_by_tup) - before_symbol_count
  grouped_symbols = [None] * (len(symbols) + len_delta)
  dest_index = 0
  src_index = 0
  seen_tups = set()
  replacement_tup_by_index = {}
  for name_tup, replacement_list in replacements_by_tup.iteritems():
    for tup in replacement_list:
      replacement_tup_by_index[tup[0]] = name_tup

  sorted_items = replacement_tup_by_index.items()
  sorted_items.sort(key=lambda tup: tup[0])
  for index, name_tup in sorted_items:
    count = index - src_index
    grouped_symbols[dest_index:dest_index + count] = (
        symbols[src_index:src_index + count])
    src_index = index + 1
    dest_index += count
    if name_tup not in seen_tups:
      seen_tups.add(name_tup)
      group_symbols = [tup[1] for tup in replacements_by_tup[name_tup]]
      grouped_symbols[dest_index] = symbols._CreateTransformed(
          group_symbols, name=name_tup[1], full_name=name_tup[2],
          section_name=name_tup[0])
      dest_index += 1

  assert len(grouped_symbols[dest_index:None]) == len(symbols[src_index:None])
  grouped_symbols[dest_index:None] = symbols[src_index:None]
  logging.debug('Finished clustering symbols.')
  return grouped_symbols


from lxml import etree as ET
from argparse import ArgumentParser
import sys

parser = ArgumentParser(description="Add revision Description to tei files")
parser.add_argument("-s", "--spelling", action="store_true")
parser.add_argument("files", nargs="+")
args = parser.parse_args(sys.argv[1:])

changes = [{"text": "<change>Navigation bars removed</change>", "date": "2020-04-23"},
           {"text": "<change>Paragraphs from original estimated and marked up.</change>", "date": "2020-04-23"},
           {"text": "<change>Seperated sentences using <gi>s</gi> and tokenized using <gi>w</gi> using spaCy.</change>", "date": "2020-04-27"},
           {"text": "<change>POS-Tags added to <att>pos</att> of <gi>w</gi> using spaCy.</change>", "date": "2020-04-27"},
           {"text": "<change>Lemma added to <att>lemma</att> of <gi>w</gi> using spaCy.</change>", "date": "2020-04-27"},
           {"text": "<change>Stop word inidcator added to <att>type</att> of <gi>w</gi> using spaCy.</change>", "date": "2020-04-27"}]
if args.spelling:
    changes.append({"text": "<change>Implicit spelling normalization of manually chosen key terms with manually crafted rules.</change>", "date":"2020-04-24"})
    changes.sort(key= lambda x: x["date"])

ns = {"tei": "http://www.tei-c.org/ns/1.0"}
xml_parser = ET.XMLParser(remove_blank_text=True)
for file in args.files:
    with open(file, "rb") as xml_file:
        tree = ET.parse(xml_file)
    root = tree.getroot()
    teiHeader = root.find(".//tei:teiHeader", ns)
    revDesc = ET.SubElement(teiHeader, "revisionDesc")
    for c in changes:
        c_elem = ET.fromstring(c["text"])
        c_elem.attrib["when"] = c["date"]
        c_elem.attrib["who"] = "Pascal Hein"
        revDesc.append(c_elem)

    outfile = file[:-4] + "_reved.xml"
    ET.indent(root, space="    ")
    tree.write(outfile, encoding="utf-8", method="xml", xml_declaration=True)


#=================================FUNCTION DESCRITION================================
# This code is responsible for displaying the chefs' profiles, namely, all the food
# they offer, their favourite recipes, their rating, etc.
#====================================================================================

#=================INCLUDING THE NECESSARY LIBRARIES AND OTHER FILES==================
import webapp2
from google.appengine.api import memcache
from google.appengine.ext import db
from google.appengine.ext.webapp import template
from dbs.databases import *
from utils import *
import time
#====================================================================================

#===================================THE MAIN CODE====================================
class MessageInboxHandler(webapp2.RequestHandler):

    #---------------------Display the HTML Template Upon Loading---------------------
    def get(self):
        params = {}
        #----------------------Check If a User Has Signed In-------------------------
        temp = self.request.cookies.get('user_id')
        login_necessary = 0
        if temp:
            userid = valid_hash_cookie(temp)
            if userid:
                params['userid'] = userid
                user = db.GqlQuery("SELECT * FROM UserPass_User WHERE user_id = '%s'" %userid)
                user = user.get()
                params['chef_flag'] = user.ischef
            else:
                login_necessary = 1
        else:
            login_necessary = 1
        #----------------------------------------------------------------------------

        if login_necessary:
            params['return_url'] = './message_inbox'
            self.response.out.write(template.render('./html/login.html',params))

        #--------------------------If the USER ID Is Correct-------------------------
        else:

            #--------------------Find the Messages for this user---------------------
            messages = db.GqlQuery("SELECT * FROM Messages_DB WHERE recepient_id = '%s'" %userid)
            message_list = []

            if messages:
                for message in messages:
                    if message.new_conversation:
                        message_specifications = []

                        message_specifications.append(unescape_html(message.message_title))
                        m_body = unescape_html(message.message_body)
                        if (len(m_body) > 50):
                            m_body = m_body[0:50]
                            m_body = m_body + '...'
                        message_specifications.append(m_body)
                        message_specifications.append((message.sender_id))
                        message_specifications.append(int(message.read_status))
                        message_specifications.append((message.message_parent))
                        message_specifications.append((message.created_date))
                        message_specifications.append((message.key()))

                        message_list.append(message_specifications)

            messages = db.GqlQuery("SELECT * FROM Messages_DB WHERE sender_id = '%s'" %userid)

            if messages:
                for message in messages:
                    if message.new_conversation and message.message_parent:
                        message_specifications = []

                        message_specifications.append(unescape_html(message.message_title))
                        m_body = unescape_html(message.message_body)
                        if (len(m_body) > 50):
                            m_body = m_body[0:50]
                            m_body = m_body + '...'
                        message_specifications.append(m_body)
                        message_specifications.append((message.recepient_id))
                        message_specifications.append(int(1))
                        message_specifications.append((message.message_parent))
                        message_specifications.append((message.created_date))
                        message_specifications.append((message.key()))

                        message_list.append(message_specifications)
            #------------------------------------------------------------------------

            #-------------------Assign the HTML Template Parameters------------------
            params['message_list'] = message_list
            #------------------------------------------------------------------------

            #----------------------Display the Final HTML File-----------------------
            self.response.out.write(template.render('./html/message_inbox.html',params))
            #------------------------------------------------------------------------

        #----------------------------------------------------------------------------

    #--------------------------------------------------------------------------------

#====================================================================================


# coding: utf-8
from __future__ import print_function
import logging
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials

"""
import gcloud

glcoud.init()

for res in glcoud.bigquery(
    project_id="xxxxx",
        query='SELECT * FROM ladata.iscool_20151201'):
    print(res)
"""

logging.basicConfig(level=logging.INFO)
log = logging.getLogger('gcloud')
log.setLevel(logging.INFO)

conf = {}

def init():
    global conf
    conf = {
        'credentials': get_credentials(),
        'bigquery': get_bigquery(),
        'google_storage': get_storage()
    }

def get_credentials():
    global conf
    if 'credentials' not in conf:
        # [START build_service]
        # Grab the application's default credentials from the environment.
        # https://developers.google.com/identity/protocols/application-default-credentials
        # environment variable GOOGLE_APPLICATION_CREDENTIALS must be set
        conf['credentials'] = GoogleCredentials.get_application_default()
    return conf['credentials']

def get_bigquery():
    global conf
    if 'bigquery' not in conf:
        credentials = get_credentials()
        conf['bigquery'] = discovery.build('bigquery', 'v2', credentials=credentials)
    return conf['bigquery']

def get_storage():
    global conf
    if 'google_storage' not in conf:
        credentials = get_credentials()
        conf['google_storage'] = discovery.build('storage', 'v1', credentials=credentials)
    return conf['google_storage']

def bigquery(project_id, query, timeout=1000, num_retries=2):
    global conf
    bigquery = conf['bigquery']

    query_data = {
        'query': query,
        'timeoutMs': 0,  # use a timeout of 0 means we'll always need
        # to get the results via getQueryResults
    }

    response = bigquery.jobs().query(
        projectId=project_id,
        body=query_data
    ).execute(
        num_retries=num_retries
    )

    job_ref = response['jobReference']

    while True:
        page_token = response.get('pageToken', None)
        query_complete = response.get('jobComplete', False)

        if query_complete:
            for row in response['rows']:
                yield [field['v'] for field in row['f']]

            if page_token is None:
                # The query is done and there are no more results
                # to read.
                break

        response = bigquery.jobs().getQueryResults(
            pageToken=page_token,
            timeoutMs=timeout,
            **job_ref
        ).execute(
            num_retries=num_retries
        )


from bs4 import BeautifulSoup as soup
import requests
import json

print('Loading function')

def lambda_handler(event, context):
    res = []
    users = event["usernames"].split(' ')

    for user in users:
        res_current = {
            "user": user,
            "total_commits": 0,
            "days_active": 0,
            "percentage_active": 0
        }
        base_url = 'https://github.com/{}'.format(user)
        response = requests.get(base_url)

        page_soup = soup(response.text, 'html.parser')
        weeks = page_soup.findAll('rect', {'class': 'day'})
        print(weeks[-1])

        day_max = -1 * int(event["days"])
        day = -1
        while day != day_max:
            # days active
            if weeks[day]['fill'] != '#ebedf0':
                res_current["days_active"] += 1
            # total commits
            res_current["total_commits"] += int(weeks[day]["data-count"])
            day -= 1
        res_current["percentage_active"] = res_current["days_active"] / int(event["days"])
        res.append(res_current)
    print(res)
    return json.dumps(res)

# lambda_handler({"usernames": "hoganmcdonald pete-wildberger", "days": "5"}, 'context')