from reportlab.pdfgen import canvas
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import black, red
import reportlab.rl_config

reportlab.rl_config.warnOnMissingFontGlyphs = 0
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase.pdfmetrics import stringWidth
import math
import Bio
from Bio.Seq import Seq
import configs
import argparse
import configs
import os

import svist4get.drawing as drawing
import svist4get.data_processing as data_processing
import svist4get.methods as methods


class Parameters:
    def __init__(self):
        pass

    def parse_command_line(self):

        parser = argparse.ArgumentParser(prog='svist4get', description='svist4get commmand line format:')

        parser.add_argument('--sampledata', '-sampledata', dest='data', action='store_true',
                            help='Creates the \'svist4get\' folder in the current working directory.The folder will contain sample data sets and adjustable configuration file templates.')

        parser.add_argument('-t', type=str, dest='transcript_id', default=0,
                            help='<id> Visualization of the genomic window of a particular transcript (uses GTF transcript_id)')

        parser.add_argument('-g', type=str, dest='gene_id', default=0,
                            help='<id>  Visualization of the genomic window of a particular gene (uses GTF gene_id)')

        parser.add_argument('-w', dest='window', nargs='*', default=[0],
                            help=' tss|tis <upstream> <downstream>  Visualization of a genomic window centered on a transcription start site|translation initiation site of a particular transcript (using GTF transcript_id) with a fixed offsets upstream and downstream.  OR   <contig> <start> <end> Visualization an arbitrary genomic window using genomic coordinates')

        parser.add_argument('-fa', dest='fasta_file', type=str, default=0,
                            help='<path/file.fa>  Path to the genome assembly multifasta for the sequence track.')

        parser.add_argument('-gtf', dest='gtf_file', type=str, default=0,
                            help='<path/file.gtf>   Path to the gtf file with the transcript annotation')

        parser.add_argument('-bg', nargs='*', dest='bedgraph', default='', type=str,
                            help='<path/file1.bedGraph>[<path/file2.bedGraph>...]  Paths to bedGraph files for genomic signal tracks.')

        parser.add_argument('-c', dest='path_to_config', default='A4_p2', type=str,
                            help='<path/file.cfg> |A4_p1|A4_p2|A4_l   Path to a configuration file or name of the premade config file (can be A4_p1, A4_p2, A4_l) (default = A4_p2')

        parser.add_argument('-o', type=str, dest='output_filename',
                            default='svist4get_example',
                            help="Output files base name. Generates a pair of pdf (vector) and png (raster) files.")

        parser.add_argument('-hi', dest='hide_introns', action='store_true',
                            help='Hide intronic segments (default: false')

        parser.add_argument('-rc', dest='revcomp_transform', action='store_true',
                            help='Reverse-complement transformation of the genomic window (default: off)')

        parser.add_argument('-scb', dest='bedgraph_scale',
                            choices=['large', 'small', 'auto'], default='auto',
                            help=' large| small  Scaling style for bedGraph tracks (default: %(default)s)')

        parser.add_argument('-nts', dest='nucleotide_sequence', action='store_true',
                            help='Show reference genomic sequence with single-nucleotide resolution (default: for short regions e.g. < 160 nts)')

        parser.add_argument('-sca', dest='aminoacid_sequence_scale',
                            choices=['large', 'small', 'auto'], metavar=None, default='auto',
                            help=' largae|small   Scaling style for aminoacid sequence track: large (for long regions) or small (keep single-aminoacid resolution) (default: auto)')

        parser.add_argument('-it', dest='image_title', type=str, default='',
                            help=' <text>   Image title')

        parser.add_argument('-xs', dest='genome_axis_tics_step', type=int, default=0,
                            help=' <N>  X axis tics step [nt] (default: auto)')

        parser.add_argument('-lb', dest='lvl_of_bedgraph_axis_tics', nargs='*', default=-1, type=float,
                            help=' <N1>[<N2>...]   Y axis tics levels for a bedGraph track, a space-separated list of values, a separate value for each bedGraph track (default: auto)')

        parser.add_argument('-ys', dest='bedgraph_axis_tics_step', nargs='*', type=float, default=0,
                            help=' <N1> [<N2>...]    Step between axis tics for a bedgraph track Y-axis, a space-separated list of values, a separated value for each bedgraph track (default:auto)')

        parser.add_argument('-bul', dest='bedgraph_upper_limit', nargs='*', default=0,
                            help=' <N1> <N2>...| max   Upper limit for a bedGraph track Y-axis, can be: 1. a space-separated list of values: a separate axis limit for each bedGraph track; 2.a maximum reachable value across all visible tracks. (default: reachable maximum for each track separately)')

        parser.add_argument('-bll', dest='bedgraph_lower_limit', nargs='*', type=float, default=0,
                            help='<N1> <N2>...  Lower limit for a bedGraph track Y-axis, a space-separated list of value, a separate value required for each bedGraph track (default: 0 for each track)')

        parser.add_argument('-bl', dest='bedgraph_label', nargs='*', default='',
                            help='<text1> <text2>... Text labels for a bedGraph tracks, space-separated list of values, a separate value required for each bedGraph track (default: names of the bedGraph files)')

        parser.add_argument('-gi', dest='genomic_intervals', nargs='*', default=[], action='append',
                            type=str,
                            help='<s-e> [<s1-e1>...]  Additional track showing selected genomic intervals as segment, space-separated list of start-end pairs (on genomic coordinates)')

        parser.add_argument('-gil', dest='genomic_intervals_label', nargs='*', default='',
                            help='<text1> <text2>...  Labels for genomic intervals, space-separated list of string, a separate value required for each  genomic intervals track')

        parser.add_argument('-st', dest='selected_transcripts', nargs='*', default=0,
                            help=' <transcript_id1> <transcript_id2>... A complete list of transcript IDs to shor (other will be hidden, applicable to -w and -g modes)')

        parser.add_argument('-hrf', dest='highlight_reading_frame',
                            help=' <N>   Highlight a particular reading frame (+0, +1 or +2)', default=0)

        parser.add_argument('-hc', dest='highlight_codon', type=str, default='',
                            help='<NtNtNt>   Highlight selected codon on the aminoacid sequence track, e.g. -hc ATT')

        parser.add_argument('-hf', dest='highlight_frame', nargs=3, default=0,
                            help='<x1> <x2> <label> Highlight and label a given segment of the genomic window (global coordinates on the selected contig)')

        args = parser.parse_args()
        args = vars(args)
        self.cmd = args



    def load_config(self, path_to_config):
        current_directory = os.getcwd()
        internal_directory = os.path.join(os.path.dirname(__file__), 'svist4get_data')

        if path_to_config == 'A4_p1' or path_to_config == 'A4_p2' or path_to_config == 'A4_l':
            name_of_config = path_to_config + '.cfg'
            path_to_config = internal_directory + '/' + name_of_config

        else:
            path_to_config = path_to_config

        config = configs.load(path_to_config)
        config = config.get_config()

        config = config['root']

        # ---------
        config['additional_title'] = ''
        config['highlight_codon'] = ''
        config['bedgraph_label'] = ''
        config['genomic_intervals'] = []
        config['genomic_intervals_label'] = ''
        config['selected_transcripts'] = 0
        config['bedgraph'] = ''
        config['transcript_id'] = 0
        config['gene_id'] = 0
        config['window'] = [0]
        config['highlight_reading_frame'] = 0
        config['highlight_frame'] = 0

        # ------------
        config['path_to_config'] = path_to_config

        self.config = config

    def load_palette(self):
        current_directory = os.getcwd()
        internal_directory = os.path.join(os.path.dirname(__file__), 'svist4get_data')

        path_to_config = self.config['path_to_config']

        path_to_palette = self.config['palette']
        path_to_dir_with_config = os.path.dirname(path_to_config)

        path_to_palette = os.path.join(path_to_dir_with_config, path_to_palette)

        palette = configs.load(path_to_palette)

        palette = palette.get_config()
        palette = palette['root']
        self.palette = palette

    def load_triplet_code(self):
        current_directory = os.getcwd()
        internal_directory = os.path.join(os.path.dirname(__file__), 'svist4get_data')

        path_to_config = self.config['path_to_config']

        path_to_triplet_code = self.config['triplet_code']
        path_to_dir_with_config = os.path.dirname(path_to_config)

        path_to_triplet_code = os.path.join(path_to_dir_with_config, path_to_triplet_code)

        self.config['triplet_code'] = path_to_triplet_code

        self.path_to_triplet_code = path_to_triplet_code

    def update_config(self):
        self.config.update(self.cmd)

    def load_font(self):
        current_directory = os.getcwd()
        internal_directory = os.path.join(os.path.dirname(__file__), 'files')
        path_to_font_regular = self.config['font_regular']
        path_to_font_medium = self.config['font_medium']

        path_to_config = self.config['path_to_config']
        path_to_dir_with_config = os.path.dirname(path_to_config)

        path_to_font_regular = os.path.join(path_to_dir_with_config, path_to_font_regular)
        path_to_font_medium = os.path.join(path_to_dir_with_config, path_to_font_medium)

        paths_to_fonts = dict(regular=path_to_font_regular, medium=path_to_font_medium)

        self.paths_to_fonts = paths_to_fonts

    def add_gtf_data(self, gtf_data):
        self.gtf = gtf_data

    def create_pdf(self):
        pdf = canvas.Canvas(self.config['output_filename'], pagesize=(self.config['page_width'] * cm, 0 * cm))
        self.pdf = pdf

    def initialize(self, path_to_config):
        self.load_config(path_to_config)
        self.load_palette()
        self.load_triplet_code()
        self.load_font()
        self.create_pdf()


class Data_preparation:
    def __init__(self, parameters):
        self.parameters = parameters

    def create_dict(self):
        pass


class Loader:
    def __init__(self, preparated_data):
        self.global_parameters = preparated_data['global_parameters']
        self.adjustable_parameters = preparated_data['adjustable_parameters']
        self.config = preparated_data['config']
        self.palette = preparated_data['palette']
        self.paths_to_fonts = preparated_data['paths_to_fonts']

    def extract_data(self):
        pass

    def translation_of_coordinate(self):
        pass

    def hide_introns(self):
        pass

    def revcomp_transform(self):
        pass

    def create_track(self):
        pass


class Vgrid_data_preparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_dict(self):
        pdf = self.parameters.pdf

        global_parameters = dict(pdf=pdf)
        adjustable_parameters = dict()
        config = self.parameters.config
        palette = self.parameters.palette
        paths_to_fonts = self.parameters.paths_to_fonts
        preparated_data = dict(adjustable_parameters=adjustable_parameters, global_parameters=global_parameters,
                               config=config, palette=palette, paths_to_fonts=paths_to_fonts)
        return (preparated_data)


class Vgrid_loader(Loader):
    def __init__(self, preparated_data):
        super().__init__(preparated_data)

    def extract_data(self):
        pass

    def translation_of_coordinate(self):
        pass

    def revcomp_transform(self):
        pass

    def hide_introns(self):
        pass

    def create_track(self):
        pdf = self.global_parameters['pdf']
        input_data = dict(pdf=pdf)

        track = drawing.Vertical_grid(input_data, self.config, self.palette, self.paths_to_fonts)
        tracks = []
        tracks.append(track)
        return (tracks)


class Title_data_preparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_dict(self):
        start = self.parameters.gtf['start']
        end = self.parameters.gtf['end']
        scaffold = self.parameters.gtf['name_of_scaffold']
        strand = '(+)'
        pdf = self.parameters.pdf
        name_of_transcript = ''

        additional_info = self.parameters.config['image_title']

        global_parameters = dict(start=start, end=end, scaffold=scaffold, pdf=pdf, strand=strand,
                                 additional_info=additional_info)
        adjustable_parameters = dict()
        preparated_data = dict(global_parameters=global_parameters, adjustable_parameters=adjustable_parameters,
                               config=self.parameters.config, palette=self.parameters.palette,
                               paths_to_fonts=self.parameters.paths_to_fonts)

        return (preparated_data)


class Title_loader(Loader):
    def __init__(self, preparated_data):
        super().__init__(preparated_data)

    def extract_data(self):
        pass

    def translation_of_coordinate(self):
        pass

    def revcomp_transform(self):
        self.global_parameters['strand'] = '(-)'

    def hide_introns(self):
        pass

    def create_track(self):
        tracks = []
        input_data = dict()
        input_data.update(self.adjustable_parameters)
        input_data.update(self.global_parameters)

        track = drawing.Title(input_data, self.config, self.palette, self.paths_to_fonts)
        tracks.append(track)
        return (tracks)


class Axis_tics_data_praparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_dict(self):

        adjustable_parameters = []
        for i in self.parameters.gtf['for_transcript']:
            dictonary = dict(exons=i['exons'])
            adjustable_parameters.append(dictonary)

        step = self.parameters.config['genome_axis_tics_step']
        end = int(self.parameters.gtf['end'])
        start = int(self.parameters.gtf['start'])
        pdf = self.parameters.pdf

        count = start
        coordinates = []
        for i in range(end - start):
            coordinates.append(count)
            count += 1

        global_parameters = dict(pdf=pdf, step=step, start=start, end=end, coordinates=coordinates)

        preparated_data = dict(global_parameters=global_parameters, adjustable_parameters=adjustable_parameters,
                               config=self.parameters.config, palette=self.parameters.palette,
                               paths_to_fonts=self.parameters.paths_to_fonts)
        return (preparated_data)


class Axis_tics_loader(Loader):

    def __init__(self, preparated_data):
        super().__init__(preparated_data)

    def extract_data(self):
        self.coordinates = self.global_parameters['coordinates']
        self.start = self.global_parameters['start']
        self.end = self.global_parameters['end']

    def translation_of_coordinate(self):
        self.start = int(self.global_parameters['start'])
        self.end = int(self.global_parameters['end'])
        start = self.start
        end = self.end
        self.start = int(start) - int(start)
        self.end = int(end) - int(start)

        for k in range(len(self.adjustable_parameters)):

            exons_row = self.adjustable_parameters[k]['exons'].split(';')
            exons = ''
            if exons_row[0] != '':
                for j in exons_row:
                    i = j.split('-')

                    if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                            self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):

                        exons += str(self.global_parameters['start']) + '-' + str(i[1])

                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                            self.global_parameters['end']):
                        exons += str(i[0]) + '-' + str(i[1])

                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[0]) < int(
                            self.global_parameters['end']) and int(i[1]) >= int(self.global_parameters['end']):

                        exons += str(i[0]) + '-' + str(self.global_parameters['end'])

                        exons += ';'
                    elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                            self.global_parameters['end']):
                        exons += str(self.global_parameters['start']) + '-' + (str(self.global_parameters['end']))
                        exons += ';'
                new_exon = ''
                for i in exons.split(';'):
                    if i != '':
                        new_exon += i
                        if i != exons.split(';')[::-1][1]:
                            new_exon += ';'
                exons = new_exon
            else:
                self.exons = ''

            exons = exons.split(';')
            correct_exons = ''

            if len(exons) != 0:
                for j in exons:
                    if j != '':
                        i = j.split('-')
                        s = int(i[0])
                        e = int(i[1])
                        left = s - int(start)
                        right = e - int(start)
                        correct_exons += (str(left) + '-' + str(right))
                        if j != exons[::-1][0]:
                            correct_exons += ';'
                self.exons = correct_exons

            self.adjustable_parameters[k]['exons'] = self.exons.split(';')

    def hide_introns(self):

        line = []
        for i in range(self.end - self.start):
            line.append(i)

        for i in range(len(self.adjustable_parameters)):
            exons = self.adjustable_parameters[i]['exons']
            new_line = []
            for j in exons:
                if j != '':
                    j = j.split('-')
                    start = int(j[0])
                    end = int(j[1])
                    new_line += line[start:end]

            new_line = sorted(new_line)

            introns = []
            count = 0
            if new_line == []:
                introns.append(str(self.start) + '-' + str(self.end))

            for k in range(len(new_line)):
                if count == 0 and new_line[0] != self.start:
                    count += 1
                    introns.append(str(self.start) + '-' + str(new_line[0]))

                if k != len(new_line) - 1:
                    if new_line[k] != (new_line[k + 1] - 1):
                        start = int(new_line[k]) + 1
                        end = new_line[k + 1]
                        introns.append(str(start) + '-' + str(end))
                else:
                    if new_line[len(new_line) - 1] != self.end - 1:
                        start = int(new_line[len(new_line) - 1]) + 1
                        end = self.end
                        introns.append(str(start) + '-' + str(end))
            self.adjustable_parameters[i]['introns'] = introns

        intersect_introns = []
        for i in range(len(self.adjustable_parameters)):
            if i == 0:
                intersect_introns += set(self.adjustable_parameters[i]['introns'])

            if i == 0:

                adjust_param = []
                for k in self.adjustable_parameters[i]['introns']:
                    k = k.split('-')
                    start = int(k[0])
                    end = int(k[1])
                    for l in range(end - start):
                        adjust_param.append(start)
                        start += 1
                intersect_introns1 = adjust_param

            if i != 0:
                adjust_param = []

                for p in self.adjustable_parameters[i]['introns']:
                    p = p.split('-')
                    start = int(p[0])
                    end = int(p[1])
                    for q in range(end - start):
                        adjust_param.append(start)
                        start += 1

            intersect_introns1 = (set(intersect_introns1) & set(adjust_param))

        intersect_introns4 = []
        for o in intersect_introns1:
            intersect_introns4.append(o)
        intersect_introns2 = intersect_introns4

        intersect_introns3 = []
        for z in range(len(intersect_introns2) - 1):
            if z == 0:
                start = int(intersect_introns2[0])
            if intersect_introns2[z] != intersect_introns2[z + 1] - 1:
                end = int(intersect_introns2[z]) + 1
                intersect_introns3.append(str(start) + '-' + str(end))
                start = int(intersect_introns2[z + 1])
            if z == len(intersect_introns2) - 1 and end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1]) + 1

                intersect_introns3.append(str(start) + '-' + str(end))
            if z == (len(intersect_introns2) - 2) and self.end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1] + 1)
                intersect_introns3.append(str(start) + '-' + str(end))

        intersect_introns = intersect_introns3

        true_exon = []
        new_line = []
        for i in intersect_introns:
            if i != '':
                i = i.split('-')
                start = int(i[0])
                end = int(i[1])
                new_line += line[start:end]
        new_line = sorted(new_line)
        count = 0
        if new_line == []:
            true_exon.append(str(self.start) + '-' + str(self.end))
        for i in range(len(new_line)):
            if count == 0 and new_line[0] != self.start:
                count += 1
                true_exon.append(str(self.start) + '-' + str(new_line[0]))
            if i != len(new_line) - 1:
                if new_line[i] != (new_line[i + 1] - 1):
                    start = int(new_line[i]) + 1
                    end = new_line[i + 1]
                    true_exon.append(str(start) + '-' + str(end))
            else:
                if new_line[len(new_line) - 1] != self.end - 1:
                    start = int(new_line[len(new_line) - 1]) + 1
                    end = self.end
                    true_exon.append(str(start) + '-' + str(end))

        new_coordinates = []
        if true_exon != []:

            for i in true_exon:
                SandE = i.split('-')
                start = int(SandE[0])
                end = int(SandE[1])
                new_coordinates += self.coordinates[start:end]
        self.coordinates = new_coordinates

    def revcomp_transform(self):

        self.reverse = 1

        length = len(self.coordinates)

        for k in range(len(self.adjustable_parameters)):

            new_exon = ''
            self.exons = self.adjustable_parameters[k]['exons']
            for i in self.exons:
                if i != '':
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])

                    new_start = (length - end)
                    new_end = (length - start)
                    new_exon += str(new_start) + '-' + str(new_end)
                    if i != self.exons[::-1][0]:
                        new_exon += ';'
            self.adjustable_parameters[k]['exons'] = new_exon.split(';')

        self.coordinates = self.coordinates[::-1]

    def create_track(self):
        tracks = []
        pdf = self.global_parameters['pdf']
        coordinates = self.coordinates
        step = self.global_parameters['step']
        input_data = dict(pdf=pdf, coordinates=coordinates, step=step)

        track = drawing.Axis_tics(input_data, self.config, self.palette, self.paths_to_fonts)
        tracks.append(track)
        return (tracks)


class Nt_seq_data_preparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)
        self.fasta_file = parameters.config['fasta_file']

    def create_dict(self):
        start = self.parameters.gtf['start']
        end = self.parameters.gtf['end']
        pdf = self.parameters.pdf
        scaffold = self.parameters.gtf['name_of_scaffold']

        coordinates = []
        count = start
        for i in range(end - start):
            coordinates.append(count)
            count += 1

        adjustable_parameters = []
        for i in self.parameters.gtf['for_transcript']:
            dictonary = dict(exons=i['exons'])
            adjustable_parameters.append(dictonary)

        global_parameters = dict(scaffold=scaffold, start=start, end=end, pdf=pdf, coordinates=coordinates,
                                 fasta_file=self.fasta_file)

        preparated_data = dict(global_parameters=global_parameters, adjustable_parameters=adjustable_parameters,
                               config=self.parameters.config, palette=self.parameters.palette,
                               paths_to_fonts=self.parameters.paths_to_fonts)

        return (preparated_data)


class Nt_seq_loader(Loader):
    def __init__(self, preparated_data):
        super().__init__(preparated_data)
        self.path_to_fasta = self.global_parameters['fasta_file']

    def extract_data(self):

        name_of_file = self.path_to_fasta
        name_of_scaffold = self.global_parameters['scaffold']
        start = self.global_parameters['start']
        end = self.global_parameters['end']
        sequnce_obj = data_processing.Extraction_of_sequence(name_of_file, name_of_scaffold, start, end)
        self.sequence = sequnce_obj.extract()
        self.reverse = 0
        self.coordinates = self.global_parameters['coordinates']

    def translation_of_coordinate(self):
        for k in range(len(self.adjustable_parameters)):
            self.start = int(self.global_parameters['start'])
            self.end = int(self.global_parameters['end'])
            start = self.start
            end = self.end
            self.start = int(start) - int(start)
            self.end = int(end) - int(start)
            exons_row = self.adjustable_parameters[k]['exons'].split(';')
            exons = ''
            if exons_row[0] != '':
                for j in exons_row:
                    i = j.split('-')

                    if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                            self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):

                        exons += str(self.global_parameters['start']) + '-' + str(i[1])

                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                            self.global_parameters['end']):
                        exons += str(i[0]) + '-' + str(i[1])

                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[0]) < int(
                            self.global_parameters['end']) and int(i[1]) >= int(self.global_parameters['end']):

                        exons += str(i[0]) + '-' + str(self.global_parameters['end'])

                        exons += ';'
                    elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                            self.global_parameters['end']):
                        exons += str(self.global_parameters['start']) + '-' + (str(self.global_parameters['end']))
                        exons += ';'
                new_exon = ''
                for i in exons.split(';'):
                    if i != '':
                        new_exon += i
                        if i != exons.split(';')[::-1][1]:
                            new_exon += ';'
                exons = new_exon
            else:
                self.exons = ''

            exons = exons.split(';')
            correct_exons = ''

            if len(exons) != 0:
                for j in exons:
                    if j != '':
                        i = j.split('-')
                        s = int(i[0])
                        e = int(i[1])
                        left = s - int(start)
                        right = e - int(start)
                        correct_exons += (str(left) + '-' + str(right))
                        if j != exons[::-1][0]:
                            correct_exons += ';'
                self.exons = correct_exons

            self.adjustable_parameters[k]['exons'] = self.exons.split(';')

    def hide_introns(self):

        line = []
        for i in range(self.end - self.start):
            line.append(i)

        for i in range(len(self.adjustable_parameters)):
            exons = self.adjustable_parameters[i]['exons']
            new_line = []
            for j in exons:
                if j != '':
                    j = j.split('-')
                    start = int(j[0])
                    end = int(j[1])
                    new_line += line[start:end]

            new_line = sorted(new_line)

            introns = []
            count = 0
            if new_line == []:
                introns.append(str(self.start) + '-' + str(self.end))

            for k in range(len(new_line)):
                if count == 0 and new_line[0] != self.start:
                    count += 1
                    introns.append(str(self.start) + '-' + str(new_line[0]))

                if k != len(new_line) - 1:
                    if new_line[k] != (new_line[k + 1] - 1):
                        start = int(new_line[k]) + 1
                        end = new_line[k + 1]
                        introns.append(str(start) + '-' + str(end))
                else:
                    if new_line[len(new_line) - 1] != self.end - 1:
                        start = int(new_line[len(new_line) - 1]) + 1
                        end = self.end
                        introns.append(str(start) + '-' + str(end))
            self.adjustable_parameters[i]['introns'] = introns

        intersect_introns = []
        for i in range(len(self.adjustable_parameters)):
            if i == 0:
                intersect_introns += set(self.adjustable_parameters[i]['introns'])
            intersect_introns = (set(intersect_introns) & set(self.adjustable_parameters[i]['introns']))

            if i == 0:

                adjust_param = []
                for k in self.adjustable_parameters[i]['introns']:
                    k = k.split('-')
                    start = int(k[0])
                    end = int(k[1])
                    for l in range(end - start):
                        adjust_param.append(start)
                        start += 1
                intersect_introns1 = adjust_param

            if i != 0:
                adjust_param = []

                for p in self.adjustable_parameters[i]['introns']:
                    p = p.split('-')
                    start = int(p[0])
                    end = int(p[1])
                    for q in range(end - start):
                        adjust_param.append(start)
                        start += 1

            intersect_introns1 = (set(intersect_introns1) & set(adjust_param))

        intersect_introns4 = []
        for o in intersect_introns1:
            intersect_introns4.append(o)
        intersect_introns2 = intersect_introns4

        intersect_introns3 = []
        for z in range(len(intersect_introns2) - 1):
            if z == 0:
                start = int(intersect_introns2[0])
            if intersect_introns2[z] != intersect_introns2[z + 1] - 1:
                end = int(intersect_introns2[z]) + 1
                intersect_introns3.append(str(start) + '-' + str(end))
                start = int(intersect_introns2[z + 1])
            if z == len(intersect_introns2) - 1 and end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1]) + 1

                intersect_introns3.append(str(start) + '-' + str(end))
            if z == (len(intersect_introns2) - 2) and self.end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1] + 1)
                intersect_introns3.append(str(start) + '-' + str(end))

        intersect_introns = intersect_introns3

        true_exon = []
        new_line = []
        for i in intersect_introns:
            if i != '':
                i = i.split('-')
                start = int(i[0])
                end = int(i[1])
                new_line += line[start:end]
        new_line = sorted(new_line)
        count = 0
        if new_line == []:
            true_exon.append(str(self.start) + '-' + str(self.end))
        for i in range(len(new_line)):
            if count == 0 and new_line[0] != self.start:
                count += 1
                true_exon.append(str(self.start) + '-' + str(new_line[0]))
            if i != len(new_line) - 1:
                if new_line[i] != (new_line[i + 1] - 1):
                    start = int(new_line[i]) + 1
                    end = new_line[i + 1]
                    true_exon.append(str(start) + '-' + str(end))
            else:
                if new_line[len(new_line) - 1] != self.end - 1:
                    start = int(new_line[len(new_line) - 1]) + 1
                    end = self.end
                    true_exon.append(str(start) + '-' + str(end))

        self.exon = true_exon

        row_sequence = self.sequence
        row_coordinates = self.coordinates
        coordinates = []
        sequence = ''
        for i in self.exons:
            SandE = i.split('-')
            start = int(SandE[0])
            end = int(SandE[1])
            coordinates += row_coordinates[start:end]
            sequence += row_sequence[start:end]
        self.sequence = sequence
        self.coordinates = coordinates

    def revcomp_transform(self):

        length = len(self.coordinates)

        for k in range(len(self.adjustable_parameters)):

            new_exon = ''
            self.exons = self.adjustable_parameters[k]['exons']
            for i in self.exons:
                if i != '':
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])

                    new_start = (length - end)
                    new_end = (length - start)
                    new_exon += str(new_start) + '-' + str(new_end)
                    if i != self.exons[::-1][0]:
                        new_exon += ';'
            self.adjustable_parameters[k]['exons'] = new_exon.split(';')

        self.reverse = 1
        self.coordinates = self.coordinates[::-1]
        row_sequence = Seq(self.sequence)
        sequence = row_sequence.reverse_complement()
        self.sequence = str(sequence)

    def create_track(self):
        tracks = []
        sequence = self.sequence
        pdf = self.global_parameters['pdf']
        input_data = dict(sequence=sequence, pdf=pdf)

        track = drawing.Nucleotide_sequence(input_data, self.config, self.palette, self.paths_to_fonts)
        tracks.append(track)

        return (tracks)


class Transcript_struct_data_preparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_dict(self):
        start = self.parameters.gtf['start']
        end = self.parameters.gtf['end']
        pdf = self.parameters.pdf
        scaffold = self.parameters.gtf['name_of_scaffold']
        coordinates = []
        count = start
        for i in range(end - start):
            coordinates.append(count)
            count += 1

        adjustable_parameters = []
        for i in self.parameters.gtf['for_transcript']:
            dictonary = dict(coordinates=coordinates, exons=i['exons'], CDS=i['CDS'], strand=i['strand'],
                             name=i['name_of_transcript'], start=i['start_of_transcript'], end=i['end_of_transcript'])
            adjustable_parameters.append(dictonary)

        global_parameters = dict(scaffold=scaffold, start=start, end=end, pdf=pdf)

        preparated_data = dict(global_parameters=global_parameters, adjustable_parameters=adjustable_parameters,
                               config=self.parameters.config, palette=self.parameters.palette,
                               paths_to_fonts=self.parameters.paths_to_fonts)

        return (preparated_data)


class Transcript_struct_loader(Loader):
    def __init__(self, preparated_data):
        super().__init__(preparated_data)

    def extract_data(self):

        name_of_scaffold = self.global_parameters['scaffold']
        start = self.global_parameters['start']
        end = self.global_parameters['end']
        self.reverse = 0

        for i in range(len(self.adjustable_parameters)):
            self.start_t = self.adjustable_parameters[i]['start']
            self.end_t = self.adjustable_parameters[i]['end']
            self.transcript = str(self.start_t) + '-' + str(self.end_t)
            self.adjustable_parameters[i]['transcript'] = self.transcript
            self.coordinates = self.adjustable_parameters[0]['coordinates']

    def translation_of_coordinate(self):

        for k in range(len(self.adjustable_parameters)):
            self.start = int(self.global_parameters['start'])
            self.end = int(self.global_parameters['end'])
            start = self.start
            end = self.end
            self.start = int(start) - int(start)
            self.end = int(end) - int(start)
            exons_row = self.adjustable_parameters[k]['exons'].split(';')
            exons = ''
            if exons_row[0] != '':
                for j in exons_row:
                    i = j.split('-')

                    if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                            self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):

                        exons += str(self.global_parameters['start']) + '-' + str(i[1])

                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                            self.global_parameters['end']):
                        exons += str(i[0]) + '-' + str(i[1])

                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[0]) < int(
                            self.global_parameters['end']) and int(i[1]) >= int(self.global_parameters['end']):

                        exons += str(i[0]) + '-' + str(self.global_parameters['end'])

                        exons += ';'
                    elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                            self.global_parameters['end']):
                        exons += str(self.global_parameters['start']) + '-' + (str(self.global_parameters['end']))
                        exons += ';'
                new_exon = ''
                for i in exons.split(';'):
                    if i != '':
                        new_exon += i
                        if i != exons.split(';')[::-1][1]:
                            new_exon += ';'
                exons = new_exon
            else:
                self.exons = ''

            exons = exons.split(';')
            correct_exons = ''

            if len(exons) != 0:
                for j in exons:
                    if j != '':
                        i = j.split('-')
                        s = int(i[0])
                        e = int(i[1])
                        left = s - int(start)
                        right = e - int(start)
                        correct_exons += (str(left) + '-' + str(right))
                        if j != exons[::-1][0]:
                            correct_exons += ';'
                self.exons = correct_exons

            self.adjustable_parameters[k]['exons'] = self.exons.split(';')

            CDS_row = self.adjustable_parameters[k]['CDS'].split(';')

            CDS = ''
            if CDS_row[0] != '':
                for j in CDS_row:
                    i = j.split('-')

                    if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                            self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):

                        CDS += str(self.global_parameters['start']) + '-' + str(i[1])

                        CDS += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                            self.global_parameters['end']):
                        CDS += str(i[0]) + '-' + str(i[1])

                        CDS += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[0]) < int(
                            self.global_parameters['end']) and int(i[1]) >= int(self.global_parameters['end']):

                        CDS += str(i[0]) + '-' + str(self.global_parameters['end'])

                        CDS += ';'
                    elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                            self.global_parameters['end']):
                        CDS += str(self.global_parameters['start']) + '-' + (str(self.global_parameters['end']))
                        CDS += ';'
                new_CDS = ''
                for i in CDS.split(';'):
                    if i != '':
                        new_CDS += i
                        if i != CDS.split(';')[::-1][1]:
                            new_CDS += ';'
                CDS = new_CDS

            else:
                CDS = ''
            CDS = CDS.split(';')
            correct_CDS = ''

            if len(CDS[0]) != 0:
                for j in CDS:
                    i = j.split('-')
                    s = int(i[0])
                    e = int(i[1])
                    left = s - int(start)
                    right = e - int(start)
                    correct_CDS += (str(left) + '-' + str(right))
                    if j != CDS[::-1][0]:
                        correct_CDS += ';'
                self.CDS = correct_CDS
            else:
                self.CDS = ''

            self.adjustable_parameters[k]['CDS'] = self.CDS.split(';')
            transcript_row = self.adjustable_parameters[k]['transcript'].split(';')
            if transcript_row != ['0-0']:
                transcript = ''
                if transcript_row != '':

                    for j in transcript_row:
                        i = j.split('-')

                        if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                                self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):
                            transcript += str(self.global_parameters['start']) + '-' + str(i[1])
                            # transcript += ';'
                        elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                                self.global_parameters['end']):
                            transcript += str(i[0]) + '-' + str(i[1])
                            # transcript += ';'
                        elif int(i[0]) > int(self.global_parameters['start']) and int(i[0]) < int(
                                self.global_parameters['end']) and int(i[1]) > int(self.global_parameters['end']):
                            transcript += str(i[0]) + '-' + str(self.global_parameters['end'])
                            # transcript += ';'
                        elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                                self.global_parameters['end']):
                            transcript += str(self.global_parameters['start']) + '-' + (
                                str(self.global_parameters['end']))
                            # transcript += ';'
                    # new_transcript = ''
                    # for i in transcript.split(';'):
                    # if i!= '':
                    # new_transcript += i
                    # if i!=transcript.split(';')[::-1][1]:
                    # new_transcript+=';'
                    # transcript = new_transcript
                transcript = transcript.split(';')
                correct_transcript = ''
                for j in transcript:
                    if j != '':
                        i = j.split('-')
                        s = int(i[0])
                        e = int(i[1])
                        left = s - int(start)
                        right = e - int(start)
                        correct_transcript += (str(left) + '-' + str(right))
                        # if j!= transcript[::-1][0]:
                        # correct_transcript += ';'
                self.transcript = correct_transcript

                self.adjustable_parameters[k]['transcript'] = self.transcript
            else:
                self.adjustable_parameters[k]['transcript'] == ['0-0']

    def hide_introns(self):

        line = []
        for i in range(self.end - self.start):
            line.append(i)

        for i in range(len(self.adjustable_parameters)):
            exons = self.adjustable_parameters[i]['exons']
            new_line = []
            for j in exons:
                if j != '':
                    j = j.split('-')
                    start = int(j[0])
                    end = int(j[1])
                    new_line += line[start:end]

            new_line = sorted(new_line)
            introns = []
            count = 0
            if new_line == []:
                introns.append(str(self.start) + '-' + str(self.end))

            for k in range(len(new_line)):
                if count == 0 and new_line[0] != self.start:
                    count += 1
                    introns.append(str(self.start) + '-' + str(new_line[0]))

                if k != len(new_line) - 1:
                    if new_line[k] != (new_line[k + 1] - 1):
                        start = int(new_line[k]) + 1
                        end = new_line[k + 1]
                        introns.append(str(start) + '-' + str(end))
                else:
                    if new_line[len(new_line) - 1] != self.end - 1:
                        start = int(new_line[len(new_line) - 1]) + 1
                        end = self.end
                        introns.append(str(start) + '-' + str(end))
            self.adjustable_parameters[i]['introns'] = introns
        intersect_introns = []
        for i in range(len(self.adjustable_parameters)):
            if i == 0:
                intersect_introns += set(self.adjustable_parameters[i]['introns'])

            intersect_introns = (set(intersect_introns) & set(self.adjustable_parameters[i]['introns']))

            if i == 0:

                adjust_param = []
                for k in self.adjustable_parameters[i]['introns']:
                    k = k.split('-')
                    start = int(k[0])
                    end = int(k[1])
                    for l in range(end - start):
                        adjust_param.append(start)
                        start += 1
                intersect_introns1 = adjust_param

            if i != 0:
                adjust_param = []

                for p in self.adjustable_parameters[i]['introns']:
                    p = p.split('-')
                    start = int(p[0])
                    end = int(p[1])
                    for q in range(end - start):
                        adjust_param.append(start)
                        start += 1

            intersect_introns1 = (set(intersect_introns1) & set(adjust_param))

        intersect_introns4 = []
        for o in intersect_introns1:
            intersect_introns4.append(o)
        intersect_introns2 = intersect_introns4

        intersect_introns3 = []
        for z in range(len(intersect_introns2) - 1):
            if z == 0:
                start = int(intersect_introns2[0])
            if intersect_introns2[z] != intersect_introns2[z + 1] - 1:
                end = int(intersect_introns2[z]) + 1
                intersect_introns3.append(str(start) + '-' + str(end))
                start = int(intersect_introns2[z + 1])
            if z == len(intersect_introns2) - 1 and end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1]) + 1

                intersect_introns3.append(str(start) + '-' + str(end))
            if z == (len(intersect_introns2) - 2) and self.end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1] + 1)
                intersect_introns3.append(str(start) + '-' + str(end))

        intersect_introns = intersect_introns3

        true_exon = []
        new_line = []
        for i in intersect_introns:
            if i != '':
                i = i.split('-')
                start = int(i[0])
                end = int(i[1])
                new_line += line[start:end]
        new_line = sorted(new_line)
        count = 0
        if new_line == []:
            true_exon.append(str(self.start) + '-' + str(self.end))
        for i in range(len(new_line)):
            if count == 0 and new_line[0] != self.start:
                count += 1
                true_exon.append(str(self.start) + '-' + str(new_line[0]))
            if i != len(new_line) - 1:
                if new_line[i] != (new_line[i + 1] - 1):
                    start = int(new_line[i]) + 1
                    end = new_line[i + 1]
                    true_exon.append(str(start) + '-' + str(end))
            else:
                if new_line[len(new_line) - 1] != self.end - 1:
                    start = int(new_line[len(new_line) - 1]) + 1
                    end = self.end
                    true_exon.append(str(start) + '-' + str(end))

        self.exons = true_exon
        for k in range(len(self.adjustable_parameters)):
            row_coordinates = self.coordinates
            coordinates = []

            for i in self.exons:
                SandE = i.split('-')
                start = int(SandE[0])
                end = int(SandE[1])
                coordinates += row_coordinates[start:end]
            self.adjustable_parameters[k]['coordinates'] = coordinates

            exist_exon = []

            for i in range(self.end - self.start):
                exist_exon.append(1)

            for i in self.exons:
                SandE = i.split('-')
                start = int(SandE[0])
                end = int(SandE[1])
                start_for_end = []
                count = start

                for j in range((end - start)):
                    start_for_end.append(count)
                    count += 1

                for j in start_for_end:
                    exist_exon[j] = 0

            count = 1
            for i in range(len(exist_exon)):
                if exist_exon[i] == 1:
                    exist_exon[i] = count
                    count += 1

            for i in range(len(exist_exon)):
                if i != 0:
                    if exist_exon[i] == 0:
                        exist_exon[i] = exist_exon[i - 1]

            exist_exon = exist_exon[::-1]
            exist_exon.append(0)
            exist_exon = exist_exon[::-1]

            exons = self.adjustable_parameters[k]['exons']
            new_exons = ''
            for i in exons:
                SandE = i.split('-')
                start = int(SandE[0])
                end = int(SandE[1])
                new_start = start - exist_exon[start]
                new_end = end - exist_exon[end]
                new_exons += str(new_start) + '-' + str(new_end)
                if i != exons[::-1][0]:
                    new_exons += ';'

            self.adjustable_parameters[k]['exons'] = new_exons.split(';')

            CDS = self.adjustable_parameters[k]['CDS']
            new_CDS = ''
            if CDS[0] != '':
                for i in CDS:
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])
                    new_start = start - exist_exon[start]
                    new_end = end - exist_exon[end]
                    new_CDS += str(new_start) + '-' + str(new_end)
                    if i != CDS[::-1][0]:
                        new_CDS += ';'

                self.CDS = new_CDS
            else:
                self.CDS = ''
            self.adjustable_parameters[k]['CDS'] = self.CDS.split(';')

            transcript = self.adjustable_parameters[k]['transcript']

            new_transcript = ''
            i = transcript
            if i != '':
                SandE = i.split('-')
                start = int(SandE[0])
                end = int(SandE[1])
                new_start = start - exist_exon[start]
                new_end = end - exist_exon[end]
                new_transcript += str(new_start) + '-' + str(new_end)

            self.adjustable_parameters[k]['transcript'] = new_transcript

    def revcomp_transform(self):
        self.adjustable_parameters = self.adjustable_parameters[::-1]
        for k in range(len(self.adjustable_parameters)):
            self.reverse = 1
            self.adjustable_parameters[k]['coordinates'] = self.adjustable_parameters[k]['coordinates'][::-1]

            length = len(self.adjustable_parameters[k]['coordinates'])
            new_exon = ''
            self.exons = self.adjustable_parameters[k]['exons']
            for i in self.exons:
                if i != '':
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])

                    new_start = (length - end)
                    new_end = (length - start)
                    new_exon += str(new_start) + '-' + str(new_end)
                    if i != self.exons[::-1][0]:
                        new_exon += ';'
            self.adjustable_parameters[k]['exons'] = new_exon.split(';')[::-1]

            new_CDS = ''
            self.CDS = self.adjustable_parameters[k]['CDS']
            if self.CDS[0] != '':
                for i in self.CDS:
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])

                    new_start = (length - end)
                    new_end = (length - start)
                    new_CDS += str(new_start) + '-' + str(new_end)
                    if i != self.CDS[::-1][0]:
                        new_CDS += ';'
            self.adjustable_parameters[k]['CDS'] = new_CDS.split(';')[::-1]

            new_transcript = ''

            self.transcript = self.adjustable_parameters[k]['transcript']
            i = self.transcript
            if i != '':
                SandE = i.split('-')
                start = int(SandE[0])
                end = int(SandE[1])

                new_start = (length - end)
                new_end = (length - start)
                new_transcript += str(new_start) + '-' + str(new_end)

            self.adjustable_parameters[k]['transcript'] = new_transcript
        self.adjustable_parameters = self.adjustable_parameters

    def create_track(self):
        tracks = []
        length = len(self.coordinates)
        self.length_of_sequence = length
        pdfmetrics.registerFont(TTFont('font_medium', self.paths_to_fonts['medium']))
        pdfmetrics.registerFont(TTFont('font_regular', self.paths_to_fonts['regular']))

        self.available_width = self.config['page_width'] * cm
        self.width_of_space = ((self.available_width - 2 * self.config['margin'] * cm) / self.length_of_sequence) * 0.1

        width_of_space = self.width_of_space

        self.width_of_box = ((self.available_width - 2 * self.config[
            'margin'] * cm + self.width_of_space) - self.width_of_space * self.length_of_sequence) / self.length_of_sequence
        width_of_box = self.width_of_box

        for k in range(len(self.adjustable_parameters)):
            self.adjustable_parameters[k]['intersect'] = 1
            if k != (len(self.adjustable_parameters) - 1):
                regions1 = self.adjustable_parameters[k]['transcript']
                regions2 = self.adjustable_parameters[k + 1]['transcript']
                regions1 = regions1.split('-')
                regions2 = regions2.split('-')
                start1 = min(int(regions1[0]), int(regions1[1]))
                end1 = max(int(regions1[0]), int(regions1[1]))
                start2 = min(int(regions2[0]), int(regions2[1]))
                end2 = max(int(regions2[0]), int(regions2[1]))
                self.name1 = self.adjustable_parameters[k]['name']
                self.name2 = self.adjustable_parameters[k + 1]['name']
                self.global_parameters['pdf'].setFont('font_medium', self.config['font_size_transcript_id'])
                width_of_string1 = stringWidth(self.name1, 'font_medium', self.config['font_size_transcript_id'])
                width_of_string2 = stringWidth(self.name1, 'font_medium', self.config['font_size_transcript_id'])
                width1 = (end1 - start1) * width_of_box
                width2 = (end2 - start2) * width_of_box
                first = []

                l = start1
                for i in range(end1 - start1):
                    first.append(l)
                    l += 1
                second = []
                p = start2
                for i in range(end2 - start2):
                    second.append(p)
                    p += 1
                result = list(set(first) & set(second))
                a1 = min(first)
                a2 = max(first)
                b1 = min(second)
                b2 = max(second)
                length1 = end1 - start1
                length2 = end2 - start2
                dist = min(abs(b1 - a1), abs(b1 - a2), abs(b2 - a1), abs(b2 - a2))

                plus1 = 0
                plus2 = 0
                if width_of_string1 > width1:
                    plus1 = width_of_string1 - width1
                if width_of_string2 > width2:
                    plus2 = width_of_string2 - width2
                dist1 = dist * width_of_box

                if len(result) == 0 and abs(dist) > 0.02 * length and (plus1 + plus2) < dist1:
                    self.adjustable_parameters[k]['intersect'] = 0

        for k in range(len(self.adjustable_parameters)):
            exons = self.adjustable_parameters[k]['exons']
            coordinates = self.adjustable_parameters[k]['coordinates']
            CDS = self.adjustable_parameters[k]['CDS']
            # end = len(sequence) + 1
            # start = 0
            i = self.adjustable_parameters[k]['transcript']
            if i != '':
                i = i.split('-')
                left = i[0]
                right = i[1]
            else:
                left = len(coordinates)
                right = 0
            intersect = self.adjustable_parameters[k]['intersect']
            delete = str(0) + '-' + str(left) + ';' + str(right) + '-' + str(len(coordinates))
            delete = delete.split(';')
            i = self.adjustable_parameters[k]['transcript'].split('-')
            pdf = self.global_parameters['pdf']
            reverse = self.reverse
            name = self.adjustable_parameters[k]['name']
            orientation = self.adjustable_parameters[k]['strand']
            input_data = dict(exons=exons, CDS=CDS, coordinates=coordinates, intersect=intersect, transcript=i,
                              delete=delete,
                              reverse=reverse, name_of_transcript=name, orientation=orientation, pdf=pdf)
            track = drawing.Transcript_structure(input_data, self.config, self.palette, self.paths_to_fonts)
            tracks.append(track)
        return (tracks)


class Bedgraph_data_preparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_dict(self):
        start = self.parameters.gtf['start']
        end = self.parameters.gtf['end']
        step = self.parameters.config['bedgraph_axis_tics_step']
        lvl_of_tics = self.parameters.config['lvl_of_bedgraph_axis_tics']
        self.maxmax = 0
        if self.parameters.config['bedgraph_upper_limit'] != 0:
            if self.parameters.config['bedgraph_upper_limit'][0] == 'max':
                self.parameters.config['bedgraph_upper_limit'] = 0
                self.maxmax = 1
        upper_limit = self.parameters.config['bedgraph_upper_limit']
        bottom_limit = self.parameters.config['bedgraph_lower_limit']
        scaffold = self.parameters.gtf['name_of_scaffold']
        all_exons = []
        for l in range(len(self.parameters.gtf['for_transcript'])):
            exons = self.parameters.gtf['for_transcript'][l]['exons']
            dictonary = dict(exons=exons)
            all_exons.append(dictonary)
        pdf = self.parameters.pdf

        adjustable_parameters = []
        if self.parameters.config['bedgraph_label'] == '':
            self.parameters.config['bedgraph_label'] = []
            for q in self.parameters.config['bedgraph']:
                q = os.path.abspath(q)
                q = os.path.basename(q)
                q = os.path.splitext(q)[0]
                self.parameters.config['bedgraph_label'].append(q)

        for i in range(len(self.parameters.config['bedgraph'])):

            if self.parameters.config['bedgraph_upper_limit'] == 0:
                upper_limit = 0
            else:
                upper_limit = self.parameters.config['bedgraph_upper_limit'][i]

            if self.parameters.config['bedgraph_lower_limit'] == 0:
                bottom_limit = 0
            else:
                bottom_limit = self.parameters.config['bedgraph_lower_limit'][i]

            if self.parameters.config['bedgraph_axis_tics_step'] == 0:
                step = 0
            else:
                step = self.parameters.config['bedgraph_axis_tics_step'][i]

            dictonary = dict(information=self.parameters.config['bedgraph_label'][i],
                             file_name=self.parameters.config['bedgraph'][i], upper_limit=upper_limit,
                             bottom_limit=bottom_limit, step=step)
            adjustable_parameters.append(dictonary)

        global_parameters = dict(start=start, end=end, lvl_of_tics=lvl_of_tics, exons=all_exons, scaffold=scaffold,
                                 pdf=pdf)

        preparated_data = dict(global_parameters=global_parameters, adjustable_parameters=adjustable_parameters,
                               config=self.parameters.config, palette=self.parameters.palette,
                               paths_to_fonts=self.parameters.paths_to_fonts)
        if self.maxmax == 1:
            self.parameters.config['bedgraph_upper_limit'] = ['max']
        return (preparated_data)


class Bedgraph_loader(Loader):
    def __init__(self, preparated_data):
        super().__init__(preparated_data)

    def extract_data(self):
        self.coverages = []
        for i in range(len(self.adjustable_parameters)):
            self.path_to_bedgraph = self.adjustable_parameters[i]['file_name']
            with open(self.path_to_bedgraph) as bedgraph:
                pos_of_start = []
                length_of_interval = []
                coverage_on_interval = []
                self.found = 0
                for line in bedgraph:
                    line = line.strip()
                    line = line.split('\t')

                    if line[0] == self.global_parameters['scaffold'] and (
                            int(line[1]) >= self.global_parameters['start']) and (
                            int(line[2]) <= self.global_parameters['end']):
                        pos_of_start.append(int(line[1]) - self.global_parameters['start'])
                        length_of_interval.append(int(line[2]) - int(line[1]))
                        coverage_on_interval.append((line[3]))
                        self.found = 1

                if self.found == 0:
                    if 'chr' in self.global_parameters['scaffold']:
                        self.global_parameters['scaffold'] = self.global_parameters['scaffold'][3::]
                    else:
                        self.global_parameters['scaffold'] = 'chr' + self.global_parameters['scaffold']
                for line in bedgraph:
                    line = line.strip()
                    line = line.split('\t')

                    if line[0] == self.global_parameters['scaffold'] and (
                            int(line[1]) >= self.global_parameters['start']) and (
                            int(line[2]) <= self.global_parameters['end']):
                        pos_of_start.append(int(line[1]) - self.global_parameters['start'])
                        length_of_interval.append(int(line[2]) - int(line[1]))
                        coverage_on_interval.append((line[3]))

            coverage = [0 for i in range(int(self.global_parameters['end']) - int(self.global_parameters['start']))]
            for i in range(len(pos_of_start)):
                for q in range(length_of_interval[i]):
                    coverage[pos_of_start[i] + q] = coverage_on_interval[i]

            coverage_str = ''
            count = 0
            for i in coverage:
                count += 1
                coverage_str += str(i)
                if count != len(coverage):
                    coverage_str += ','

            self.coverage = coverage_str.split(',')
            self.coverages.append(self.coverage)

    def translation_of_coordinate(self):

        start = self.global_parameters['start']
        end = self.global_parameters['end']
        self.start = int(start) - int(start)
        self.end = int(end) - int(start)
        self.exons = self.global_parameters['exons']
        for k in range(len(self.exons)):

            exons_row = self.exons[k]['exons'].split(';')
            exons = ''
            if exons_row != '':
                for j in exons_row:
                    i = j.split('-')
                    if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                            self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):
                        exons += str(self.global_parameters['start']) + '-' + str(i[1])
                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                            self.global_parameters['end']):
                        exons += str(i[0]) + '-' + str(i[1])
                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[0]) < int(
                            self.global_parameters['end']) and int(i[1]) >= int(self.global_parameters['end']):
                        exons += str(i[0]) + '-' + str(self.global_parameters['end'])
                        exons += ';'
                    elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                            self.global_parameters['end']):
                        exons += str(self.global_parameters['start']) + '-' + (str(self.global_parameters['end']))
                        exons += ';'

                new_exon = ''
                for i in exons.split(';'):
                    if i != '':
                        new_exon += i
                        if i != exons.split(';')[::-1][1]:
                            new_exon += ';'
                exons = new_exon

            exons = exons.split(';')
            correct_exons = ''

            for j in exons:
                if j != '':
                    q = j.split('-')
                    s = int(q[0])
                    e = int(q[1])
                    left = s - int(start)
                    right = e - int(start)
                    correct_exons += (str(left) + '-' + str(right))
                    if j != exons[::-1][0]:
                        correct_exons += ';'
            exons = correct_exons

            exons = exons.split(';')
            self.exons[k]['exons'] = exons

    def hide_introns(self):
        self.introns = []
        line = []
        for i in range(len(self.coverages[0])):
            line.append(i)

        for l in range(len(self.exons)):
            exons = self.exons[l]['exons'][::-1]
            new_line = []
            for j in exons:
                if j != '':
                    j = j.split('-')
                    start = int(j[0])
                    end = int(j[1])
                    new_line += line[start:end]

            new_line = sorted(new_line)

            introns = []
            count = 0
            if new_line == []:
                introns.append(str(self.start) + '-' + str(self.end))

            for k in range(len(new_line)):
                if count == 0 and new_line[0] != self.start:
                    count += 1
                    introns.append(str(self.start) + '-' + str(new_line[0]))

                if k != len(new_line) - 1:
                    if new_line[k] != (new_line[k + 1] - 1):
                        start = int(new_line[k]) + 1
                        end = new_line[k + 1]
                        introns.append(str(start) + '-' + str(end))
                else:
                    if new_line[len(new_line) - 1] != self.end - 1:
                        start = int(new_line[len(new_line) - 1]) + 1
                        end = self.end
                        introns.append(str(start) + '-' + str(end))
            self.introns.append(dict(introns=introns))

        intersect_introns = []
        for i in range(len(self.introns)):
            if i == 0:
                intersect_introns += set(self.introns[i]['introns'])

            intersect_introns = (set(intersect_introns) & set(self.introns[i]['introns']))

            if i == 0:

                adjust_param = []
                for k in self.introns[i]['introns']:
                    k = k.split('-')
                    start = int(k[0])
                    end = int(k[1])
                    for l in range(end - start):
                        adjust_param.append(start)
                        start += 1
                intersect_introns1 = adjust_param

            if i != 0:
                adjust_param = []

                for p in self.introns[i]['introns']:
                    p = p.split('-')
                    start = int(p[0])
                    end = int(p[1])
                    for q in range(end - start):
                        adjust_param.append(start)
                        start += 1

            intersect_introns1 = (set(intersect_introns1) & set(adjust_param))

        intersect_introns4 = []
        for o in intersect_introns1:
            intersect_introns4.append(o)
        intersect_introns2 = intersect_introns4

        intersect_introns3 = []
        for z in range(len(intersect_introns2) - 1):
            if z == 0:
                start = int(intersect_introns2[0])
            if intersect_introns2[z] != intersect_introns2[z + 1] - 1:
                end = int(intersect_introns2[z]) + 1
                intersect_introns3.append(str(start) + '-' + str(end))
                start = int(intersect_introns2[z + 1])
            if z == len(intersect_introns2) - 1 and end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1]) + 1

                intersect_introns3.append(str(start) + '-' + str(end))
            if z == (len(intersect_introns2) - 2) and self.end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1] + 1)
                intersect_introns3.append(str(start) + '-' + str(end))

        intersect_introns = intersect_introns3

        true_exons = []
        new_line = []
        for i in intersect_introns:
            if i != '':
                i = i.split('-')
                start = int(i[0])
                end = int(i[1])
                new_line += line[start:end]
        new_line = sorted(new_line)
        count = 0
        if new_line == []:
            true_exons.append(str(self.start) + '-' + str(self.end))
        for i in range(len(new_line)):
            if count == 0 and new_line[0] != self.start:
                count += 1
                true_exons.append(str(self.start) + '-' + str(new_line[0]))
            if i != len(new_line) - 1:
                if new_line[i] != (new_line[i + 1] - 1):
                    start = int(new_line[i]) + 1
                    end = new_line[i + 1]
                    true_exons.append(str(start) + '-' + str(end))
            else:
                if new_line[len(new_line) - 1] != self.end - 1:
                    start = int(new_line[len(new_line) - 1]) + 1
                    end = self.end
                    true_exons.append(str(start) + '-' + str(end))

        row_coverages = self.coverages
        coverages = []
        for self.coverage in self.coverages:
            coverage = []
            for i in true_exons:
                SandE = i.split('-')
                start = int(SandE[0])
                end = int(SandE[1])
                coverage += self.coverage[start:end]
            self.coverage = coverage
            coverages.append(self.coverage)
        self.coverages = coverages

    def revcomp_transform(self):

        length = (self.end - self.start)

        for k in range(len(self.exons)):

            new_exon = ''
            exons = self.exons[k]['exons']
            for i in exons:
                if i != '':
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])

                    new_start = (length - end)
                    new_end = (length - start)
                    new_exon += str(new_start) + '-' + str(new_end)
                    if i != exons[::-1][0]:
                        new_exon += ';'
            self.exons[k]['exons'] = new_exon.split(';')

        coverages = []
        for coverage in self.coverages:
            coverage = coverage[::-1]
            coverages.append(coverage)
        self.coverages = coverages

    def create_track(self):
        tracks = []
        count1 = 0
        for k in range(len(self.adjustable_parameters)):

            if count1 != (len(self.config['c_column'])):
                color = self.config['c_column'][count1]
            else:
                count1 = 0
                color = self.config['c_column'][count1]
            count1 += 1

            self.coverage = self.coverages[k]

            if self.global_parameters['lvl_of_tics'] == -1:
                if self.adjustable_parameters[k]['upper_limit'] == 0:
                    coverage = []
                    for i in self.coverage:
                        coverage.append(float(i))
                    sorted_coverage = sorted(coverage)
                    max = 0

                else:
                    max = self.adjustable_parameters[k]['upper_limit']

                if self.adjustable_parameters[k]['step'] == 0:
                    coverage = []
                    for i in self.coverage:
                        coverage.append(float(i))
                    min = self.adjustable_parameters[k]['bottom_limit']
                    if (max - min) >= 3:
                        step = 0
                    else:
                        step = 0
                else:
                    step = self.adjustable_parameters[k]['step']

                axis_tics = []
                min = min = self.adjustable_parameters[k]['bottom_limit']
                count = min
                if step != 0:
                    while count < max:
                        count += step
                        axis_tics.append(count)
                else:
                    axis_tics = [min]

            else:
                axis_tics = self.global_parameters['lvl_of_tics']
                min = self.adjustable_parameters[k]['bottom_limit']
                max = self.adjustable_parameters[k]['upper_limit']

            pdf = self.global_parameters['pdf']
            coverage = self.coverage
            information = self.adjustable_parameters[k]['information']

            min = min
            max = max

            input_data = dict(pdf=self.global_parameters['pdf'], color=color, coverage=self.coverage, upper_limit=max,
                              bottom_limit=min, axis_tics=axis_tics, information=information)

            track = drawing.Bedgraph(input_data, self.config, self.palette, self.paths_to_fonts)

            tracks.append(track)

        return (tracks)


class Aa_seq_data_preparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)
        self.fasta_file = self.parameters.config['fasta_file']

    def create_dict(self):
        adjustable_parameters = []
        for i in self.parameters.gtf['for_transcript']:
            dictonary = dict(exons=i['exons'])
            adjustable_parameters.append(dictonary)

        start = self.parameters.gtf['start']
        end = self.parameters.gtf['end']
        pdf = self.parameters.pdf
        scaffold = self.parameters.gtf['name_of_scaffold']
        add_codon = self.parameters.config['highlight_codon']
        highlight_reading_frame = self.parameters.config['highlight_reading_frame']
        global_parameters = dict(fasta_file=self.fasta_file, scaffold=scaffold, start=start, add_codon=add_codon,
                                 end=end, pdf=pdf, highlight_reading_frame=highlight_reading_frame)

        preparated_data = dict(global_parameters=global_parameters, adjustable_parameters=adjustable_parameters,
                               config=self.parameters.config, palette=self.parameters.palette,
                               paths_to_fonts=self.parameters.paths_to_fonts)

        return (preparated_data)


class Aa_seq_loader(Loader):
    def __init__(self, preparated_data):
        super().__init__(preparated_data)
        self.path_to_fasta = self.global_parameters['fasta_file']

    def extract_data(self):
        name_of_file = self.path_to_fasta
        name_of_scaffold = self.global_parameters['scaffold']
        start = self.global_parameters['start']
        end = self.global_parameters['end']
        sequnce_obj = data_processing.Extraction_of_sequence(name_of_file, name_of_scaffold, start, end)
        self.sequence = sequnce_obj.extract()

    def translation_of_coordinate(self):
        for k in range(len(self.adjustable_parameters)):
            self.start = int(self.global_parameters['start'])
            self.end = int(self.global_parameters['end'])
            start = self.start
            end = self.end
            self.start = int(start) - int(start)
            self.end = int(end) - int(start)
            exons_row = self.adjustable_parameters[k]['exons'].split(';')
            exons = ''
            if exons_row[0] != '':
                for j in exons_row:
                    i = j.split('-')

                    if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                            self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):

                        exons += str(self.global_parameters['start']) + '-' + str(i[1])

                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                            self.global_parameters['end']):
                        exons += str(i[0]) + '-' + str(i[1])

                        exons += ';'
                    elif int(i[0]) >= int(self.global_parameters['start']) and int(i[0]) < int(
                            self.global_parameters['end']) and int(i[1]) >= int(self.global_parameters['end']):

                        exons += str(i[0]) + '-' + str(self.global_parameters['end'])

                        exons += ';'
                    elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                            self.global_parameters['end']):
                        exons += str(self.global_parameters['start']) + '-' + (str(self.global_parameters['end']))
                        exons += ';'
                new_exon = ''
                for i in exons.split(';'):
                    if i != '':
                        new_exon += i
                        if i != exons.split(';')[::-1][1]:
                            new_exon += ';'
                exons = new_exon
            else:
                self.exons = ''

            exons = exons.split(';')
            correct_exons = ''

            if len(exons) != 0:
                for j in exons:
                    if j != '':
                        i = j.split('-')
                        s = int(i[0])
                        e = int(i[1])
                        left = s - int(start)
                        right = e - int(start)
                        correct_exons += (str(left) + '-' + str(right))
                        if j != exons[::-1][0]:
                            correct_exons += ';'
                self.exons = correct_exons

            self.adjustable_parameters[k]['exons'] = self.exons.split(';')

    def hide_introns(self):

        line = []
        for i in range(self.end - self.start):
            line.append(i)

        for i in range(len(self.adjustable_parameters)):
            exons = self.adjustable_parameters[i]['exons']
            new_line = []
            for j in exons:
                if j != '':
                    j = j.split('-')
                    start = int(j[0])
                    end = int(j[1])
                    new_line += line[start:end]

            new_line = sorted(new_line)

            introns = []
            count = 0
            if new_line == []:
                introns.append(str(self.start) + '-' + str(self.end))

            for k in range(len(new_line)):
                if count == 0 and new_line[0] != self.start:
                    count += 1
                    introns.append(str(self.start) + '-' + str(new_line[0]))

                if k != len(new_line) - 1:
                    if new_line[k] != (new_line[k + 1] - 1):
                        start = int(new_line[k]) + 1
                        end = new_line[k + 1]
                        introns.append(str(start) + '-' + str(end))
                else:
                    if new_line[len(new_line) - 1] != self.end - 1:
                        start = int(new_line[len(new_line) - 1]) + 1
                        end = self.end
                        introns.append(str(start) + '-' + str(end))
            self.adjustable_parameters[i]['introns'] = introns

        intersect_introns = []
        for i in range(len(self.adjustable_parameters)):
            if i == 0:
                intersect_introns += set(self.adjustable_parameters[i]['introns'])

            intersect_introns = (set(intersect_introns) & set(self.adjustable_parameters[i]['introns']))

            if i == 0:

                adjust_param = []
                for k in (self.adjustable_parameters[i]['introns']):
                    k = k.split('-')
                    start = int(k[0])
                    end = int(k[1])
                    for l in range(end - start):
                        adjust_param.append(start)
                        start += 1
                intersect_introns1 = adjust_param

            if i != 0:
                adjust_param = []

                for p in (self.adjustable_parameters[i]['introns']):
                    p = p.split('-')
                    start = int(p[0])
                    end = int(p[1])
                    for q in range(end - start):
                        adjust_param.append(start)
                        start += 1

            intersect_introns1 = (set(intersect_introns1) & set(adjust_param))

        intersect_introns4 = []
        for o in intersect_introns1:
            intersect_introns4.append(o)
        intersect_introns2 = intersect_introns4

        intersect_introns3 = []
        for z in range(len(intersect_introns2) - 1):
            if z == 0:
                start = int(intersect_introns2[0])
            if intersect_introns2[z] != intersect_introns2[z + 1] - 1:
                end = int(intersect_introns2[z]) + 1
                intersect_introns3.append(str(start) + '-' + str(end))
                start = int(intersect_introns2[z + 1])
            if z == len(intersect_introns2) - 1 and end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1]) + 1

                intersect_introns3.append(str(start) + '-' + str(end))
            if z == (len(intersect_introns2) - 2) and self.end != int(intersect_introns2[z + 1]):
                end = int(intersect_introns2[z + 1] + 1)
                intersect_introns3.append(str(start) + '-' + str(end))

        intersect_introns = intersect_introns3

        true_exon = []
        new_line = []
        for i in intersect_introns:
            if i != '':
                i = i.split('-')
                start = int(i[0])
                end = int(i[1])
                new_line += line[start:end]
        new_line = sorted(new_line)
        count = 0
        if new_line == []:
            true_exon.append(str(self.start) + '-' + str(self.end))
        for i in range(len(new_line)):
            if count == 0 and new_line[0] != self.start:
                count += 1
                true_exon.append(str(self.start) + '-' + str(new_line[0]))
            if i != len(new_line) - 1:
                if new_line[i] != (new_line[i + 1] - 1):
                    start = int(new_line[i]) + 1
                    end = new_line[i + 1]
                    true_exon.append(str(start) + '-' + str(end))
            else:
                if new_line[len(new_line) - 1] != self.end - 1:
                    start = int(new_line[len(new_line) - 1]) + 1
                    end = self.end
                    true_exon.append(str(start) + '-' + str(end))

        row_sequence = self.sequence
        sequence = ''
        for i in true_exon:
            SandE = i.split('-')
            start = int(SandE[0])
            end = int(SandE[1])
            sequence += row_sequence[start:end]
        self.sequence = sequence

    def revcomp_transform(self):

        length = len(self.sequence)

        for k in range(len(self.adjustable_parameters)):

            new_exon = ''
            self.exons = self.adjustable_parameters[k]['exons']
            for i in self.exons:
                if i != '':
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])

                    new_start = (length - end)
                    new_end = (length - start)
                    new_exon += str(new_start) + '-' + str(new_end)
                    if i != self.exons[::-1][0]:
                        new_exon += ';'
            self.adjustable_parameters[k]['exons'] = new_exon.split(';')

        self.reverse = 1
        row_sequence = Seq(self.sequence)
        sequence = row_sequence.reverse_complement()
        self.sequence = str(sequence)

    def create_track(self):
        tracks = []
        highlight_reading_frame = self.global_parameters['highlight_reading_frame']
        sequence = self.sequence
        pdf = self.global_parameters['pdf']
        additional_codon = self.global_parameters['add_codon']
        input_data = dict(pdf=pdf, sequence=sequence, additional_codon=additional_codon,
                          highlight_reading_frame=highlight_reading_frame)

        if len(sequence) < 160 and self.config['zoom_level'] != 'close':
            track = drawing.Aminoacid_sequence(input_data, self.config, self.palette, self.paths_to_fonts)
        else:
            track = drawing.Codons(input_data, self.config, self.palette, self.paths_to_fonts)

        tracks.append(track)
        return (tracks)


class Genomic_intervals_data_preparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_dict(self):
        start = self.parameters.gtf['start']
        end = self.parameters.gtf['end']
        pdf = self.parameters.pdf
        scaffold = self.parameters.gtf['name_of_scaffold']
        coordinates = []
        count = start
        for i in range(end - start):
            coordinates.append(count)
            count += 1

        all_exons = []
        for i in range(len(self.parameters.gtf['for_transcript'])):
            exons = self.parameters.gtf['for_transcript'][i]['exons']
            dictonary = dict(exons=exons)
            all_exons.append(dictonary)

        adjustable_parameters = []
        for i in range(len(self.parameters.config['genomic_intervals'])):
            regions = self.parameters.config['genomic_intervals'][i]
            if self.parameters.config['genomic_intervals_label'] != '':
                information = self.parameters.config['genomic_intervals_label'][i]
            else:
                information = ''
            dictonary = dict(coordinates=coordinates, regions=regions, information=information)
            adjustable_parameters.append(dictonary)

        global_parameters = dict(scaffold=scaffold, exons=all_exons, start=start, end=end, pdf=pdf)

        preparated_data = dict(global_parameters=global_parameters, adjustable_parameters=adjustable_parameters,
                               config=self.parameters.config, palette=self.parameters.palette,
                               paths_to_fonts=self.parameters.paths_to_fonts)

        return (preparated_data)


class Genomic_intervals_data_preparation(Data_preparation):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_dict(self):
        start = self.parameters.gtf['start']
        end = self.parameters.gtf['end']
        pdf = self.parameters.pdf
        scaffold = self.parameters.gtf['name_of_scaffold']
        coordinates = []
        count = start
        for i in range(end - start):
            coordinates.append(count)
            count += 1

        all_exons = []
        for i in range(len(self.parameters.gtf['for_transcript'])):
            exons = self.parameters.gtf['for_transcript'][i]['exons']
            dictonary = dict(exons=exons)
            all_exons.append(dictonary)

        adjustable_parameters = []
        for i in range(len(self.parameters.config['genomic_intervals'])):
            regions = self.parameters.config['genomic_intervals'][i]
            if self.parameters.config['genomic_intervals_label'] != '':
                information = self.parameters.config['genomic_intervals_label'][i]
            else:
                information = ''
            dictonary = dict(coordinates=coordinates, regions=regions, information=information)
            adjustable_parameters.append(dictonary)

        global_parameters = dict(scaffold=scaffold, exons=all_exons, start=start, end=end, pdf=pdf)

        preparated_data = dict(global_parameters=global_parameters, adjustable_parameters=adjustable_parameters,
                               config=self.parameters.config, palette=self.parameters.palette,
                               paths_to_fonts=self.parameters.paths_to_fonts)

        return (preparated_data)


class Genomic_intervals_loader(Loader):
    def __init__(self, preparated_data):
        super().__init__(preparated_data)

    def extract_data(self):
        pass

    def translation_of_coordinate(self):
        if self.adjustable_parameters != []:

            self.start = int(self.global_parameters['start'])
            self.end = int(self.global_parameters['end'])
            start = self.start
            end = self.end
            self.start = self.start = self.start
            self.end = self.end - self.start

            self.exons = self.global_parameters['exons']
            for k in range(len(self.exons)):

                exons_row = self.exons[k]['exons'].split(';')
                exons = ''
                if exons_row != '':
                    for j in exons_row:
                        i = j.split('-')
                        if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                                self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):
                            exons += str(self.global_parameters['start']) + '-' + str(i[1])
                            exons += ';'
                        elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                                self.global_parameters['end']):
                            exons += str(i[0]) + '-' + str(i[1])
                            exons += ';'
                        elif int(i[0]) >= int(self.global_parameters['start']) and int(i[0]) < int(
                                self.global_parameters['end']) and int(i[1]) >= int(self.global_parameters['end']):
                            exons += str(i[0]) + '-' + str(self.global_parameters['end'])
                            exons += ';'
                        elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                                self.global_parameters['end']):
                            exons += str(self.global_parameters['start']) + '-' + (str(self.global_parameters['end']))
                            exons += ';'

                    new_exon = ''
                    for i in exons.split(';'):
                        if i != '':
                            new_exon += i
                            if i != exons.split(';')[::-1][1]:
                                new_exon += ';'
                    exons = new_exon

                exons = exons.split(';')
                correct_exons = ''

                for j in exons:
                    if j != '':
                        q = j.split('-')
                        s = int(q[0])
                        e = int(q[1])
                        left = s - int(start)
                        right = e - int(start)
                        correct_exons += (str(left) + '-' + str(right))
                        if j != exons[::-1][0]:
                            correct_exons += ';'
                exons = correct_exons

                exons = exons.split(';')
                self.exons[k]['exons'] = exons

            for k in range(len(self.adjustable_parameters)):

                regions_row = self.adjustable_parameters[k]['regions']
                regions = ''
                if regions_row[0] != '':
                    for j in regions_row:
                        i = j.split('-')

                        if int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) > int(
                                self.global_parameters['start']) and int(i[1]) <= int(self.global_parameters['end']):

                            regions += str(self.global_parameters['start']) + '-' + str(i[1])

                            regions += ';'
                        elif int(i[0]) >= int(self.global_parameters['start']) and int(i[1]) <= int(
                                self.global_parameters['end']):
                            regions += str(i[0]) + '-' + str(i[1])

                            regions += ';'
                        elif int(i[0]) >= int(self.global_parameters['start']) and int(i[0]) < int(
                                self.global_parameters['end']) and int(i[1]) >= int(self.global_parameters['end']):

                            regions += str(i[0]) + '-' + str(self.global_parameters['end'])

                            regions += ';'
                        elif int(i[0]) <= int(self.global_parameters['start']) and int(i[1]) >= int(
                                self.global_parameters['end']):
                            regions += str(self.global_parameters['start']) + '-' + (str(self.global_parameters['end']))
                            regions += ';'
                    new_regions = ''
                    for i in regions.split(';'):
                        if i != '':
                            new_regions += i
                            if i != regions.split(';')[::-1][1]:
                                new_regions += ';'
                    regions = new_regions
                else:
                    self.regions = ''

                regions = regions.split(';')
                correct_regions = ''

                if len(regions) != 0:
                    for j in regions:
                        if j != '':
                            i = j.split('-')
                            s = int(i[0])
                            e = int(i[1])
                            left = s - int(start)
                            right = e - int(start)
                            correct_regions += (str(left) + '-' + str(right))
                            if j != regions[::-1][0]:
                                correct_regions += ';'
                    self.regions = correct_regions

                self.adjustable_parameters[k]['regions'] = self.regions.split(';')

    def hide_introns(self):
        if self.adjustable_parameters != []:

            self.introns = []
            line = []
            for i in range(len(self.coverages[0])):
                line.append(i)

            for l in range(len(self.exons)):
                exons = self.exons[l]['exons'][::-1]
                new_line = []
                for j in exons:
                    if j != '':
                        j = j.split('-')
                        start = int(j[0])
                        end = int(j[1])
                        new_line += line[start:end]

                new_line = sorted(new_line)

                introns = []
                count = 0
                if new_line == []:
                    introns.append(str(self.start) + '-' + str(self.end))

                for k in range(len(new_line)):
                    if count == 0 and new_line[0] != self.start:
                        count += 1
                        introns.append(str(self.start) + '-' + str(new_line[0]))

                    if k != len(new_line) - 1:
                        if new_line[k] != (new_line[k + 1] - 1):
                            start = int(new_line[k]) + 1
                            end = new_line[k + 1]
                            introns.append(str(start) + '-' + str(end))
                    else:
                        if new_line[len(new_line) - 1] != end - 1:
                            start = int(new_line[len(new_line) - 1]) + 1
                            end = self.end
                            introns.append(str(start) + '-' + str(end))
                self.introns.append(dict(introns=introns))

            intersect_introns = []
            for i in range(len(self.introns)):
                if i == 0:
                    intersect_introns += set(self.introns[i]['introns'])

                intersect_introns = (set(intersect_introns) & set(self.introns[i]['introns']))

            true_exons = []
            new_line = []
            for i in intersect_introns:
                if i != '':
                    i = i.split('-')
                    start = int(i[0])
                    end = int(i[1])
                    new_line += line[start:end]
            new_line = sorted(new_line)
            count = 0
            if new_line == []:
                true_exons.append(str(self.start) + '-' + str(self.end))
            for i in range(len(new_line)):
                if count == 0 and new_line[0] != self.start:
                    count += 1
                    true_exons.append(str(self.start) + '-' + str(new_line[0]))
                if i != len(new_line) - 1:
                    if new_line[i] != (new_line[i + 1] - 1):
                        start = int(new_line[i]) + 1
                        end = new_line[i + 1]
                        true_exons.append(str(start) + '-' + str(end))
                else:
                    if new_line[len(new_line) - 1] != self.end - 1:
                        start = int(new_line[len(new_line) - 1]) + 1
                        end = self.end
                        true_exons.append(str(start) + '-' + str(end))

            self.exons = true_exons

            for k in range(len(self.adjustable_parameters)):
                row_coordinates = self.coordinates
                coordinates = []

                for i in self.exons:
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])
                    coordinates += row_coordinates[start:end]
                self.adjustable_parameters[k]['coordinates'] = coordinates

                exist_exon = []

                for i in range(self.end - self.start):
                    exist_exon.append(1)

                for i in self.exons:
                    SandE = i.split('-')
                    start = int(SandE[0])
                    end = int(SandE[1])
                    start_for_end = []
                    count = start

                    for j in range((end - start)):
                        start_for_end.append(count)
                        count += 1

                    for j in start_for_end:
                        exist_exon[j] = 0

                count = 1
                for i in range(len(exist_exon)):
                    if exist_exon[i] == 1:
                        exist_exon[i] = count
                        count += 1

                for i in range(len(exist_exon)):
                    if i != 0:
                        if exist_exon[i] == 0:
                            exist_exon[i] = exist_exon[i - 1]

                exist_exon = exist_exon[::-1]
                exist_exon.append(0)
                exist_exon = exist_exon[::-1]

                regions = self.adjustable_parameters[k]['regions']
                new_regions = ''
                if regions[0] != '':
                    for i in regions:
                        SandE = i.split('-')
                        start = int(SandE[0])
                        end = int(SandE[1])
                        new_start = start - exist_exon[start]
                        new_end = end - exist_exon[end]
                        new_regions += str(new_start) + '-' + str(new_end)
                        if i != regions[::-1][0]:
                            new_regions += ';'

                    self.regions = new_regions
                else:
                    self.regions = ''
                self.adjustable_parameters[k]['regions'] = self.regions.split(';')

    def revcomp_transform(self):
        if self.adjustable_parameters != []:

            length = (self.end - self.start)

            for k in range(len(self.exons)):

                new_exon = ''
                exons = self.exons[k]['exons']
                for i in exons:
                    if i != '':
                        SandE = i.split('-')
                        start = int(SandE[0])
                        end = int(SandE[1])

                        new_start = (length - end)
                        new_end = (length - start)
                        new_exon += str(new_start) + '-' + str(new_end)
                        if i != exons[::-1][0]:
                            new_exon += ';'
                self.exons[k]['exons'] = new_exon.split(';')

            for k in range(len(self.adjustable_parameters)):
                self.reverse = 1
                self.adjustable_parameters[k]['coordinates'] = self.adjustable_parameters[k]['coordinates'][::-1]

                length = len(self.adjustable_parameters[k]['coordinates'])
                new_regions = ''
                self.regions = self.adjustable_parameters[k]['regions']
                for i in self.regions:
                    if i != '':
                        SandE = i.split('-')
                        start = int(SandE[0])
                        end = int(SandE[1])

                        new_start = (length - end)
                        new_end = (length - start)
                        new_regions += str(new_start) + '-' + str(new_end)
                        if i != self.regions[::-1][0]:
                            new_regions += ';'
                self.adjustable_parameters[k]['regions'] = new_regions.split(';')[::-1]

    def create_track(self):
        if self.adjustable_parameters != []:
            tracks = []
            count1 = 0

            for k in range(len(self.adjustable_parameters)):
                self.adjustable_parameters[k]['intersect'] = 1
                if k != (len(self.adjustable_parameters) - 1):
                    regions1 = self.adjustable_parameters[k]['regions'][::-1]
                    regions2 = self.adjustable_parameters[k + 1]['regions'][::-1]
                    if len(regions1) == 1 and len(regions2) == 1:
                        start1 = int(regions1[0].split('-')[0])
                        end1 = int(regions1[0].split('-')[1])
                        start2 = int(regions2[0].split('-')[0])
                        end2 = int(regions2[0].split('-')[1])
                        first = []
                        l = end1
                        for i in range(end1 - start1):
                            first.append(k)
                            l += 1
                        second = []
                        l = end2
                        for i in range(end2 - start2):
                            second.append(k)
                            l += 1
                        result = list(set(first) & set(second))

                        if len(result) < 10:
                            self.adjustable_parameters[k]['intersect'] = 0

            for k in range(len(self.adjustable_parameters)):

                if count1 != (len(self.config['c_regions'])):
                    color = self.config['c_regions'][count1]
                else:
                    count1 = 0
                    color = self.config['c_regions'][count1]
                count1 += 1

                regions = self.adjustable_parameters[k]['regions'][::-1]
                coordinates = self.adjustable_parameters[k]['coordinates']
                information = self.adjustable_parameters[k]['information']
                intersect = self.adjustable_parameters[k]['intersect']

                pdf = self.global_parameters['pdf']
                input_data = dict(regions=regions, pdf=pdf, information=information, coordinates=coordinates,
                                  color=color, intersect=intersect)
                track = drawing.Genomic_intervals(input_data, self.config, self.palette, self.paths_to_fonts)
                tracks.append(track)
        else:
            tracks = []
        return (tracks)


class Image:
    def __init__(self, vector, parameters, y_0=0):
        self.vector = vector
        self.y_0 = y_0
        self.parameters = parameters

    def go(self):
        maxs = []
        for i in self.vector:
            y = i.needed_space()
            if type(i) == drawing.Bedgraph:
                i.draw(0)
                maxs.append(i.max1)
            if type(i) == drawing.Genomic_intervals:
                if i.intersect == 0:
                    y = -self.parameters.config['gap'] * cm
            if type(i) == drawing.Transcript_structure:
                if i.intersect == 0:
                    y = -self.parameters.config['gap'] * cm
            self.y_0 += (y + self.parameters.config['gap'] * cm)
        self.parameters.config['output_filename'] += '.pdf'
        self.real_pdf = canvas.Canvas(self.parameters.config['output_filename'], pagesize=(
            self.parameters.config['page_width'] * cm,
            self.y_0 + 2 * self.parameters.config['margin'] * cm))
        # self.real_pdf = canvas.Canvas(self.data['output_filename'], pagesize=(21*cm, 29.7*cm))

        self.y_0 += self.parameters.config['margin'] * cm

        self.exist_info = 0
        self.exist_axis = 0
        count = 0
        for i in self.vector:

            i.input_data['pdf'] = self.real_pdf
            i.canvas = self.real_pdf
            y = i.needed_space()
            if type(i) == drawing.Bedgraph:
                if self.parameters.config['bedgraph_upper_limit'] != 0:
                    if self.parameters.config['bedgraph_upper_limit'][0] == 'max':
                        maximum = max(maxs)
                        i.config['upper_limit'] = maximum
                        i.max1 = maximum
            i.draw(self.y_0)

            if type(i) == drawing.Nucleotide_sequence:
                if count == 0:
                    self.y_0_transcript_structure = self.y_0
                count += 1
            if type(i) == drawing.Title:
                self.y_0_information = self.y_0
                self.exist_info = 1
            if type(i) == drawing.Axis_tics:
                self.y_0_axis_tics = self.y_0
                self.coordinates = i.coordinates
                self.exist_axis = 1
            if type(i) == drawing.Transcript_structure:
                if count == 0:
                    self.y_0_transcript_structure = self.y_0
                count += 1
                if i.intersect == 0:
                    y = -self.parameters.config['gap'] * cm

            if type(i) == drawing.Bedgraph:
                self.coverage = i.coverage_str

            if type(i) == drawing.Genomic_intervals:
                if i.intersect == 0:
                    y = -self.parameters.config['gap'] * cm

            self.y_0 -= (y + self.parameters.config['gap'] * cm)

    def highlighting_the_frame(self):

        frame_start = int(self.parameters.config['highlight_frame'][0])
        frame_end = int(self.parameters.config['highlight_frame'][1])

        coordinates = self.coordinates
        length_of_sequence = len(coordinates)

        width_of_space = ((self.parameters.config['page_width'] * cm - 2 * self.parameters.config[
            'margin'] * cm) / length_of_sequence) * 0.1
        width_of_box = ((self.parameters.config['page_width'] * cm - 2 * self.parameters.config[
            'margin'] * cm + width_of_space) - width_of_space * length_of_sequence) / length_of_sequence

        y_upper = self.y_0_transcript_structure
        y_down = self.parameters.config['margin'] * cm + self.parameters.config['gap'] * cm

        while frame_start not in coordinates:
            if frame_start <= frame_end:
                frame_start += 1

        start = coordinates.index(frame_start)

        while frame_end not in coordinates:
            if frame_end >= frame_start:
                frame_end -= 1
        end = coordinates.index(frame_end)

        x_of_start = self.parameters.config['margin'] * cm + start * (width_of_space + width_of_box)
        x_of_end = self.parameters.config['margin'] * cm + end * (
                width_of_box + width_of_space) - width_of_space
        color_fill = (methods.hex_to_rgb(self.parameters.palette[self.parameters.config['c_fill_frame']]))

        aroundcolor = (methods.hex_to_rgb(self.parameters.palette[self.parameters.config['c_stroke_frame']]))

        self.real_pdf.setStrokeColorRGB(*aroundcolor, self.parameters.config['c_stroke_frame_alpha'])
        self.real_pdf.setDash(1)
        self.real_pdf.setLineWidth(0.1)
        self.real_pdf.setLineCap(0)

        self.real_pdf.setFillColorRGB(*color_fill, self.parameters.config['c_fill_frame_alpha'])

        self.real_pdf.rect(x_of_start, y_down, x_of_end - x_of_start, y_upper - y_down, fill=1)

        pdfmetrics.registerFont(TTFont('font_regular', self.parameters.paths_to_fonts['regular']))
        pdfmetrics.registerFont(TTFont('font_medium', self.parameters.paths_to_fonts['medium']))

        self.font_size = self.parameters.config['frame_font_size_coordinates']
        face = pdfmetrics.getFont('font_regular').face
        ascent = (face.ascent * self.font_size) / 1000.0
        descent = (face.descent * self.font_size) / 1000.0
        self.height_of_string = (ascent - descent) / 1.7
        self.real_pdf.setFont('font_regular', self.parameters.config['frame_font_size_coordinates'])

        color = methods.hex_to_rgb(self.parameters.palette[self.parameters.config['c_text_frame']])
        self.real_pdf.setStrokeColorRGB(*color, alpha=1)
        self.real_pdf.setFillColorRGB(*color, alpha=1)

        # self.real_pdf.line(x_of_start, y_down, x_of_start, y_down - self.height_of_string/2)
        # self.real_pdf.line(x_of_end, y_down, x_of_end, y_down - self.height_of_string/2)

        y_down -= 1.5 * self.height_of_string

        # self.real_pdf.drawRightString(x_of_start, y_down, str('{:,}'.format(frame_start)))
        # self.real_pdf.drawString(x_of_end, y_down, str('{:,}'.format(frame_end)))
        self.real_pdf.setFont('font_regular', self.parameters.config['frame_font_size_label'])
        y_down -= 0.3 * self.height_of_string

        x_center = (x_of_end + x_of_start) / 2

        self.real_pdf.drawCentredString(x_center, y_down, self.parameters.config['highlight_frame'][2])

    def save(self):

        print('Image successfully saved to', self.parameters.config['output_filename'])
        self.real_pdf.save()

    def draw(self):
        self.go()
        if self.parameters.config['highlight_frame'] != 0:
            self.highlighting_the_frame()
        self.save()


class Tracks_maker():
    def __init__(self, parameters):
        self.parameters = parameters

    def create_tracks(self):
        pass


class Vgrid_tracks_maker(Tracks_maker):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_tracks(self):
        obj_with_data = Vgrid_data_preparation(self.parameters)
        preparated_data = obj_with_data.create_dict()
        loader = Vgrid_loader(preparated_data)
        loader.extract_data()
        loader.translation_of_coordinate()

        if self.parameters.config['hide_introns']:
            loader.hide_introns()

        if self.parameters.config['revcomp_transform']:
            loader.revcomp_transform()

        tracks = loader.create_track()

        return (tracks)


class Title_tracks_maker(Tracks_maker):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_tracks(self):
        obj_with_data = Title_data_preparation(self.parameters)
        preparated_data = obj_with_data.create_dict()
        loader = Title_loader(preparated_data)
        loader.extract_data()
        loader.translation_of_coordinate()

        if self.parameters.config['hide_introns']:
            loader.hide_introns()

        if self.parameters.config['revcomp_transform']:
            loader.revcomp_transform()

        tracks = loader.create_track()

        return (tracks)


class Axis_tics_tracks_maker(Tracks_maker):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_tracks(self):
        obj_with_data = Axis_tics_data_praparation(self.parameters)
        preparated_data = obj_with_data.create_dict()
        loader = Axis_tics_loader(preparated_data)
        loader.extract_data()
        loader.translation_of_coordinate()

        if self.parameters.config['hide_introns']:
            loader.hide_introns()

        if self.parameters.config['revcomp_transform']:
            loader.revcomp_transform()

        tracks = loader.create_track()

        return (tracks)


class Nt_seq_tracks_maker(Tracks_maker):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_tracks(self):
        obj_with_data = Nt_seq_data_preparation(self.parameters)
        preparated_data = obj_with_data.create_dict()
        loader = Nt_seq_loader(preparated_data)
        loader.extract_data()
        loader.translation_of_coordinate()

        if self.parameters.config['hide_introns']:
            loader.hide_introns()

        if self.parameters.config['revcomp_transform']:
            loader.revcomp_transform()

        tracks = loader.create_track()

        return (tracks)


class Transcript_struct_tracks_maker(Tracks_maker):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_tracks(self):
        obj_with_data = Transcript_struct_data_preparation(self.parameters)
        preparated_data = obj_with_data.create_dict()
        loader = Transcript_struct_loader(preparated_data)
        loader.extract_data()
        loader.translation_of_coordinate()

        if self.parameters.config['hide_introns']:
            loader.hide_introns()

        if self.parameters.config['revcomp_transform']:
            loader.revcomp_transform()

        tracks = loader.create_track()
        if self.parameters.config['revcomp_transform']:
            tracks = tracks

        return (tracks)


class Bedgraph_tracks_maker(Tracks_maker):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_tracks(self):
        obj_with_data = Bedgraph_data_preparation(self.parameters)
        preparated_data = obj_with_data.create_dict()
        loader = Bedgraph_loader(preparated_data)
        loader.extract_data()
        loader.translation_of_coordinate()

        if self.parameters.config['hide_introns']:
            loader.hide_introns()

        if self.parameters.config['revcomp_transform']:
            loader.revcomp_transform()

        tracks = loader.create_track()

        return (tracks)


class Aa_seq_tracks_maker(Tracks_maker):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_tracks(self):
        obj_with_data = Aa_seq_data_preparation(self.parameters)
        preparated_data = obj_with_data.create_dict()
        loader = Aa_seq_loader(preparated_data)
        loader.extract_data()
        loader.translation_of_coordinate()

        if self.parameters.config['hide_introns']:
            loader.hide_introns()

        if self.parameters.config['revcomp_transform']:
            loader.revcomp_transform()

        tracks = loader.create_track()

        return (tracks)


class Genomic_intervals_tracks_maker(Tracks_maker):
    def __init__(self, parameters):
        super().__init__(parameters)

    def create_tracks(self):
        obj_with_data = Genomic_intervals_data_preparation(self.parameters)
        preparated_data = obj_with_data.create_dict()
        loader = Genomic_intervals_loader(preparated_data)
        loader.extract_data()
        loader.translation_of_coordinate()

        if self.parameters.config['hide_introns']:
            loader.hide_introns()

        if self.parameters.config['revcomp_transform']:
            loader.revcomp_transform()

        tracks = loader.create_track()

        return (tracks)
