#! /usr/bin/python
'''
Copyright (C) 2017 Artem Synytsyn a.synytsyn@gmail.com

#TODO: Code cleaning and refactoring

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

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

import inkex
import simplestyle, sys
from math import *
# The simplestyle module provides functions for style parsing.
from simplestyle import *


    

class Knob_Scale(inkex.Effect):
    def __init__(self):
        inkex.Effect.__init__(self)
        # General settings
        self.OptionParser.add_option("--x",
                        action="store", type="int", 
                        dest="x", default=0.0,
                        help="Center X")
        self.OptionParser.add_option("--y",
                        action="store", type="int", 
                        dest="y", default=0.0,
                        help="Center Y")
        self.OptionParser.add_option("--radius",
                        action="store", type="int", 
                        dest="radius", default=100.0,
                        help="Knob radius")
        self.OptionParser.add_option("--linewidth",
                        action="store", type="int", 
                        dest="linewidth", default=1,
                        help="")        
        self.OptionParser.add_option("--angle",
                        action="store", type="float", 
                        dest="angle", default=260.0,
                        help="Angle of the knob scale in degrees")
        self.OptionParser.add_option("--draw_arc", 
                        action="store", type="inkbool",
                        dest="draw_arc", default='True',
                        help="")
        self.OptionParser.add_option("--draw_centering_circle", 
                        action="store", type="inkbool",
                        dest="draw_centering_circle", default='False',
                        help="")
        self.OptionParser.add_option("-u", "--units",
                        action="store", type="string",
                        dest="units", default="px",
                        help="units to measure size of knob")
        # Tick settings
        self.OptionParser.add_option("--n_ticks",
                        action="store", type="int", 
                        dest="n_ticks", default=5,
                        help="")
        self.OptionParser.add_option("--ticksize",
                        action="store", type="int", 
                        dest="ticksize", default=10,
                        help="")
        self.OptionParser.add_option("--n_subticks",
                        action="store", type="int", 
                        dest="n_subticks", default=10,
                        help="")
        self.OptionParser.add_option("--subticksize",
                        action="store", type="int", 
                        dest="subticksize", default=5,
                        help="")
        self.OptionParser.add_option("--style",
                        action="store", type="string", 
                        dest="style", default='marks_outwards',
                        help="Style of marks")

        # Label settings
        self.OptionParser.add_option("--labels_enabled", 
                        action="store", type="inkbool",
                        dest="labels_enabled", default='False',
                        help="")
        self.OptionParser.add_option("--rounding_level", 
                        action="store", type="int",
                        dest="rounding_level", default=0,
                        help="")
        self.OptionParser.add_option("--text_size",
                        action="store", type="int", 
                        dest="text_size", default=1,
                        help="")
        self.OptionParser.add_option("--text_offset",
                        action="store", type="int", 
                        dest="text_offset", default=20,
                        help="")
        self.OptionParser.add_option("--start_value",
                        action="store", type="float", 
                        dest="start_value", default=0,
                        help="")
        self.OptionParser.add_option("--stop_value",
                        action="store", type="float", 
                        dest="stop_value", default=10,
                        help="")
        # Dummy
        self.OptionParser.add_option("","--tab")

    def draw_text(self, textvalue, radius, angular_position, text_size, parent):
        # Create text element
        text = inkex.etree.Element(inkex.addNS('text','svg'))
        text.text = textvalue
        
        # Set text position to center of document.
        text.set('x', str(self.x_offset + radius*cos(angular_position)))
        text.set('y', str(self.y_offset + radius*sin(angular_position) + text_size/2))
    
        # Center text horizontally with CSS style.
        style = {
                'text-align' : 'center',
                 'text-anchor': 'middle',
                 'alignment-baseline' : 'center',
                 'font-size' : str(text_size),
                 'vertical-align' : 'middle'
                 }
        
        text.set('style', formatStyle(style))
        parent.append(text)
    def draw_knob_arc(self, radius, parent, angle, transform='' ):

        start_point_angle = (angle - pi)/2.0
        end_point_angle = pi - start_point_angle
        
        style = {   'stroke'        : '#000000',
                    'stroke-width'  : str(self.options.linewidth),
                    'fill'          : 'none'            }
        ell_attribs = {'style':simplestyle.formatStyle(style),
            inkex.addNS('cx','sodipodi')        :str(self.x_offset),
            inkex.addNS('cy','sodipodi')        :str(self.y_offset),
            inkex.addNS('rx','sodipodi')        :str(radius),
            inkex.addNS('ry','sodipodi')        :str(radius),
            inkex.addNS('start','sodipodi')     :str(end_point_angle),
            inkex.addNS('end','sodipodi')       :str(start_point_angle),
            inkex.addNS('open','sodipodi')      :'true',    #all ellipse sectors we will draw are open
            inkex.addNS('type','sodipodi')      :'arc',
            'transform'                         :transform
            
                }
        ell = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), ell_attribs )    
    
    def draw_centering_circle(self, radius, parent):
        
        style = {   'stroke'        : '#000000',
                    'stroke-width'  : '1',
                    'fill'          : 'none'            }
        ell_attribs = {'style':simplestyle.formatStyle(style),
            inkex.addNS('cx','sodipodi')        :str(self.x_offset),
            inkex.addNS('cy','sodipodi')        :str(self.y_offset),
            inkex.addNS('rx','sodipodi')        :str(radius),
            inkex.addNS('ry','sodipodi')        :str(radius),
            inkex.addNS('type','sodipodi')      :'arc'            
            }
        ell = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), ell_attribs )    
    
    def draw_circle_mark(self, x_offset, y_offset, radius, mark_angle, mark_length, parent):    
        
        cx = x_offset + radius*cos(mark_angle)
        cy = y_offset + radius*sin(mark_angle)
        r = mark_length / 2.0
        
        style = { 
                'stroke': '#000000',
                'stroke-width':'0',
                'fill': '#000000' 
                }
        
        circ_attribs = {
                'style':simplestyle.formatStyle(style),
                'cx':str(cx),
                'cy':str(cy),
                'r':str(r) 
                }
        circle = inkex.etree.SubElement(parent, inkex.addNS('circle','svg'), circ_attribs )
        
    def draw_knob_line_mark(self, x_offset, y_offset, radius, mark_angle, mark_length, parent):    
        x1 = x_offset + radius*cos(mark_angle)
        y1 = y_offset + radius*sin(mark_angle)
        x2 = x_offset + (radius + mark_length)*cos(mark_angle)
        y2 = y_offset + (radius + mark_length)*sin(mark_angle)
        
        line_style   = { 'stroke': '#000000',
                         'stroke-width': str(self.options.linewidth),
                         'fill': 'none'
                       }
    
        line_attribs = {'style' : simplestyle.formatStyle(line_style),
                        inkex.addNS('label','inkscape') : "none",
                        'd' : 'M '+str(x1) +',' + 
                        str(y1) +' L '+str(x2) 
                        +','+str(y2) }
    
        line = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs )
    
    def draw_tick(self, radius, mark_angle, mark_size, parent):
        if (self.options.style == 'marks_inwards') or (self.options.style == 'marks_outwards'):
            self.draw_knob_line_mark(self.x_offset, self.y_offset, radius, mark_angle, mark_size, parent)
        elif self.options.style == 'marks_circles':
            self.draw_circle_mark(self.x_offset, self.y_offset, radius, mark_angle, mark_size, parent)
    
    def effect(self):
        
        parent = self.current_layer
        radius = self.unittouu(str(self.options.radius) + self.options.units)
        self.x_offset = self.unittouu(str(self.options.x) + self.options.units)
        self.y_offset = self.unittouu(str(self.options.y) + self.options.units)
        #print >>sys.stderr, "x_offset: %s\n"        % x_offset
        #print >>sys.stderr, "y_offset: %s\n"        % y_offset
        #radius = self.options.radius
        angle = self.options.angle*pi/180.0
        n_ticks = self.options.n_ticks
        n_subticks = self.options.n_subticks
        is_outer = True
        if self.options.style == 'marks_inwards':
            is_outer = False

        tick_length = self.unittouu(str(self.options.ticksize) + self.options.units)
        subtick_length = self.unittouu(str(self.options.subticksize) + self.options.units)
        arc_radius = radius
        
        
        # Labeling settings
        start_num = self.options.start_value
        end_num = self.options.stop_value
        text_spacing = self.unittouu(str(self.options.text_offset) + self.options.units)
        text_size = self.unittouu(str(self.options.text_size) + self.options.units)

        if not is_outer:
            subtick_radius = radius + tick_length - subtick_length
            arc_radius = radius + tick_length
        else:
            subtick_radius = radius
            arc_radius = radius
            
        if self.options.draw_arc:
            self.draw_knob_arc(arc_radius, parent, angle)
        
        if self.options.draw_centering_circle:
            self.draw_centering_circle(arc_radius + tick_length + text_size + text_spacing, parent)
            
        ticks_delta = angle / (n_ticks - 1)
        start_ticks_angle = 1.5*pi - 0.5*angle
        for tick in range(n_ticks):            
            self.draw_tick(radius, start_ticks_angle + ticks_delta*tick, 
                                tick_length, parent)        
            
            if self.options.labels_enabled:                
                if self.options.rounding_level > 0:
                    tick_text = str(round(start_num + 
                                          float(tick) * (end_num - start_num) / (n_ticks - 1), 
                                          self.options.rounding_level))
                else:
                    tick_text = str(int(start_num + float(tick) * (end_num - start_num) / (n_ticks - 1)))
                    
                self.draw_text(tick_text, radius + tick_length + text_spacing, 
                          start_ticks_angle + ticks_delta*tick, 
                          text_size,
                          parent)
            
            if tick == (n_ticks - 1):
                break
            
            subticks_delta = ticks_delta / (n_subticks + 1)
            subtick_start_angle = start_ticks_angle + ticks_delta*tick + subticks_delta            
            for subtick in range(n_subticks):
                self.draw_tick(subtick_radius, subtick_start_angle + subticks_delta*subtick, 
                                    subtick_length, parent)
             
        

if __name__ == '__main__':
    e = Knob_Scale()
    e.affect()

