#!/usr/bin/env python2


# Copyright 2016 Luke Phillips (lukerazor@hotmail.com)
# 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 St, Fifth Floor, Boston, MA  02110-1301  USA

# Extension dirs
# linux:~/.config/inkscape/extensions
# windows: [Drive]:\Program Files\Inkscape\share\extensions

import inkex
import simplestyle
import copy

CSNS = ""

inkex.NSS[u'cs'] = u'http://www.razorfoss.org/tuckboxextension/'

class EffectDimensionProvider():
	def __init__(self, effect, x = 0, y = 0):
		self.Effect = effect
		
		self.Layer = effect.current_layer
		
		self.Width = effect.options.DeckWidth
		self.Height = effect.options.DeckHeight
		self.Depth = effect.options.DeckDepth
		self.Allowance = effect.options.DeckAllowance
		
		self.X = x
		self.Y = y
		
	def MMtoUU(self, mmval):
		if hasattr(self.Effect, "unittouu"):
			return str(self.Effect.unittouu("{0}mm".format(mmval)))
		else:
			MM_TO_PIXELS = 3.5433071
		
			return str(MM_TO_PIXELS * mmval)
			
	def MaximiseHeight(self):
		if self.Height < self.Width: # always choose the smallest to be the "width"
			temp = self.Width
			self.Width = self.Height
			self.Height = temp
			
class BoxBase():
	def __init__(self, dimensionProvider):
		#create a group
		self.DimProvider = dimensionProvider
		self.Group = inkex.etree.SubElement(dimensionProvider.Layer, inkex.addNS('g','svg'), {} )
		
		self.Width = dimensionProvider.Width + dimensionProvider.Allowance
		self.Height = dimensionProvider.Height + dimensionProvider.Allowance
		self.Depth = dimensionProvider.Depth + dimensionProvider.Allowance
		self.X = dimensionProvider.X
		self.Y = dimensionProvider.Y
		self.MinY = 0
		self.MinX = 0
		
		### init some common sizes ###
		self.ThumbSize = 20
		
		# tuck flap size
		self.FlapOffset = 1.5
		self.FlapHeight = self.Depth
		if self.Depth < 7 or self.Depth > 25:
			self.FlapHeight = 20
			
		# main flap size
		self.MainFlapHeight = (4 * self.Depth)/3
		if self.MainFlapHeight < self.ThumbSize:
			self.MainFlapHeight = 24		
		
		### colour ###
		self.Fill = '#ffffff'
		self.StrokeWidth = self.DimProvider.MMtoUU(0.5)		
	
	def _CreateRectangleInMillimetres(self, height, width, x, y):
		style = {'stroke': '#000000', 'stroke-width': self.StrokeWidth, 'fill' : self.Fill}                
		attribs = {'style': simplestyle.formatStyle(style), 'height': self.DimProvider.MMtoUU(height), 'width': self.DimProvider.MMtoUU(width), 'x': self.DimProvider.MMtoUU(x), 'y': self.DimProvider.MMtoUU(y)}
		inkex.etree.SubElement(self.Group, inkex.addNS('rect','svg'), attribs )
		
	def _CreatePathinMillimetres(self, cmds):
		pathStr = ""
		for cmd in cmds:
			pathStr += cmd[0] + " "
			for coord in cmd[1:]:
				pathStr += self.DimProvider.MMtoUU(coord) + " "
			
		pathStr += "z"
		#raise Exception(pathStr)
		
		style = {'stroke': '#000000', 'stroke-width': self.StrokeWidth, 'fill' : self.Fill}             
		attribs = {'style': simplestyle.formatStyle(style), 'd': pathStr}
		inkex.etree.SubElement(self.Group, inkex.addNS('path','svg'), attribs )

class SingleFlappedTuckBox(BoxBase):
	def __init__(self, dimensionProvider):
		BoxBase.__init__(self, dimensionProvider)
		
	def Create(self):
		self.FlapOffset = 1.5
		self.FlapHeight = 20
		
		# Figure out some row and column values, 
		# note rows and cols work left to right, top to bottom
		col5 = self.X - self.Depth
		col4 = col5 - self.Width
		col3 = col4 - self.Depth
		col2 = col3 - self.Width
		col1 = col2 - self.Depth
		self.MinX = col1
		
		row4 = self.Y - self.Depth
		row3 = row4 - self.Height
		row2 = row3 - self.Depth
		row1 = row2 - self.Depth
		self.MinY = row1
		
		### COLUMN 1 ###			
		#create left glue panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col1, row3)
		
		### COLUMN 2 ###
		#create box back print panel
		self._CreatePathinMillimetres( 
			[
				["m", col2, row3], 
				["h", (self.Width - self.ThumbSize)/2],
				["c", 0, (3*self.ThumbSize)/4, self.ThumbSize, (3*self.ThumbSize)/4, self.ThumbSize, 0],
				["h", (self.Width - self.ThumbSize)/2],
				["v", self.Height],
				["h", -self.Width]
			])
		
		#create box bottom glue panel
		self._CreateRectangleInMillimetres(self.Depth, self.Width, col2, row4)
		
		### COLUMN 3 ###
		#create left flap		
		self._CreatePathinMillimetres(
			[
				["m", col3, row3], 
				["h", self.Depth],
				["l", -self.FlapOffset, -self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
			
		#create left print panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row3)
		 
		#create bottom left glue panel
		self._CreateRectangleInMillimetres(self.Depth, self.Depth, col3, row4)
		
		### COLUMN 4 ###
		#create main flap		
		self._CreatePathinMillimetres(
			[
				["m", col4, row2], 
				["c", 0, -self.MainFlapHeight, self.Width, -self.MainFlapHeight, self.Width, 0]
			])
		
		#create box top print panel
		self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row2)
		
		#create box front print panel
		self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row3)
		
		#create box bottom print panel
		self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row4)
		
		### COLUMN 5 ###	
		#create right flap		
		self._CreatePathinMillimetres(
			[
				["m", col5, row3], 
				["h", self.Depth],
				["l", -self.FlapOffset, -self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
			
		#create right print panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row3)
		 
		#create bottom right glue panel
		self._CreateRectangleInMillimetres(self.Depth, self.Depth, col5, row4)
	
class DoubleFlappedTuckBox(BoxBase):
	def __init__(self, dimensionProvider):
		BoxBase.__init__(self, dimensionProvider)
		
	def Create(self):
		
		# Figure out some row and column values, 
		# note rows and cols work left to right, top to bottom
		col5 = self.X - self.Depth
		col4 = col5 - self.Width
		col3 = col4 - self.Depth
		col2 = col3 - self.Width
		col1 = col2 - self.Depth
		self.MinX = col1
		
		row5 = self.Y - self.Depth
		row4 = row5 - self.Depth
		row3 = row4 - self.Height
		row2 = row3 - self.Depth
		row1 = row2 - self.Depth
		self.MinY = row1
		
		### COLUMN 1 ###			
		#create left glue panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col1, row3)
		
		### COLUMN 2 ###
		#create box back print panel
		self._CreatePathinMillimetres( 
			[
				["m", col2, row3], 
				["h", (self.Width - self.ThumbSize)/2],
				["c", 0, (3*self.ThumbSize)/4, self.ThumbSize, (3*self.ThumbSize)/4, self.ThumbSize, 0],
				["h", (self.Width - self.ThumbSize)/2],
				["v", self.Height],
				["h", -(self.Width - self.ThumbSize)/2],
				["c", 0, -(3*self.ThumbSize)/4, -self.ThumbSize, -(3*self.ThumbSize)/4, -self.ThumbSize, 0],
				["h", -(self.Width - self.ThumbSize)/2]
			])
		
		### COLUMN 3 ###
		#create top left flap		
		self._CreatePathinMillimetres(
			[
				["m", col3, row3], 
				["h", self.Depth],
				["l", -self.FlapOffset, -self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
			
		#create left print panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row3)
		 
		#create bottom left flap		
		self._CreatePathinMillimetres(
			[
				["m", col3, row4], 
				["h", self.Depth],
				["l", -self.FlapOffset, self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
			
		### COLUMN 4 ###
		#create top main flap
		self._CreatePathinMillimetres(
			[
				["m", col4, row2], 
				["c", 0, -self.MainFlapHeight, self.Width, -self.MainFlapHeight, self.Width, 0]
			])
		
		#create box top print panel
		self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row2)
		
		#create box front print panel
		self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row3)
		
		#create box bottom print panel
		self._CreateRectangleInMillimetres(self.Depth, self.Width, col4, row4)
		
		#create bottom main flap
		self._CreatePathinMillimetres(
			[
				["m", col4, row5], 
				["c", 0, self.MainFlapHeight, self.Width, self.MainFlapHeight, self.Width, 0]
			])
		
		### COLUMN 5 ###	
		#create top right flap		
		self._CreatePathinMillimetres(
			[
				["m", col5, row3], 
				["h", self.Depth],
				["l", -self.FlapOffset, -self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
			
		#create right print panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row3)
		 
		#create bottom right flap		
		self._CreatePathinMillimetres(
			[
				["m", col5, row4], 
				["h", self.Depth],
				["l", -self.FlapOffset, self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
					
class SlipcaseTuckBox(BoxBase):
	def __init__(self, dimensionProvider):
		BoxBase.__init__(self, dimensionProvider)
		
	def Create(self):
		self.FlapOffset = 1.5
				
		# Figure out some row and column values, 
		# note rows and cols work left to right, top to bottom
		col5 = self.X - self.Depth
		col4 = col5 - self.Width
		col3 = col4 - self.Depth
		col2 = col3 - self.Width
		col1 = col2 - self.Depth
		
		row1 = self.Y - self.Height
		
		### COLUMN 1 ###			
		#create left glue flap		
		self._CreatePathinMillimetres(
			[
				["m", col2, row1], 
				["v", self.Height],
				["l", -(self.Depth - self.FlapOffset), -self.FlapOffset],
				["v", -(self.Height - (2*self.FlapOffset))],
			])
		
		### COLUMN 2 ###
		#create box back print panel
		self._CreateRectangleInMillimetres(self.Height, self.Width, col2, row1)
		
		### COLUMN 3 ###
		#create left print panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col3, row1)
		 
		### COLUMN 4 ###
		#create box front print panel
		self._CreateRectangleInMillimetres(self.Height, self.Width, col4, row1)
		
		### COLUMN 5 ###			
		#create right print panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col5, row1)

class Matchbox(BoxBase):
	def __init__(self, dimensionProvider, numFlaps):
		BoxBase.__init__(self, dimensionProvider)
		
		self.NumFlaps = numFlaps

	def Create(self):
		if self.NumFlaps == 2:
			tuckbox = DoubleFlappedTuckBox(self.DimProvider)
		else:
			tuckbox = SingleFlappedTuckBox(self.DimProvider)
		tuckbox.Create()
				
		#################################3
		# Create matching double flap tuck box
		dimProvider = copy.copy(self.DimProvider)
		dimProvider.Width -= 2
		dimProvider.Height -= 2
		dimProvider.Depth -= 2
		dimProvider.Y = tuckbox.MinY - 20
		drawer = MatcboxDrawer(dimProvider)
		drawer.Create()
		
class MatcboxDrawer(BoxBase):
	def __init__(self, dimensionProvider):
		BoxBase.__init__(self, dimensionProvider)
		
	def Create(self):
					
		fudgeDepth = self.Depth - 2 # overlap panels should be a little smaller to avoid touching box base
		# Figure out some row and column co-ord values, 
		# note rows and cols work left to right, top to bottom, values start at 0 and go negative
		col5 = self.X - fudgeDepth 
		col4 = col5 - self.Depth
		col3 = col4 - self.Width 
		col2 = col3 - self.Depth
		col1 = col2 - fudgeDepth
		
		row5 = self.Y - fudgeDepth
		row4 = row5 - self.Depth
		row3 = row4 - self.Height
		row2 = row3 - self.Depth
		row1 = row2 - fudgeDepth
		
		### COLUMN 1 ###			
		#create left overlap panel
		self._CreateRectangleInMillimetres(self.Height, fudgeDepth, col1, row3)
		
		### COLUMN 2 ###
		#create top left flap		
		self._CreatePathinMillimetres(
			[
				["m", col2, row3], 
				["h", self.Depth],
				["l", -self.FlapOffset, -self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
		
		#create box left side print panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col2, row3)
		
		#create bottom left flap		
		self._CreatePathinMillimetres(
			[
				["m", col2, row4], 
				["h", self.Depth],
				["l", -self.FlapOffset, self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
		
		### COLUMN 3 ###

		#create top side overlap panel
		self._CreateRectangleInMillimetres(fudgeDepth, self.Width, col3, row1)
		
		#create top box side panel
		self._CreateRectangleInMillimetres(self.Depth, self.Width, col3, row2)
				
		#create box bottom
		self._CreateRectangleInMillimetres(self.Height, self.Width, col3, row3)
		
		#create bottom box side panel
		self._CreateRectangleInMillimetres(self.Depth, self.Width, col3, row4)
		
		#create bottom side overlap panel
		self._CreateRectangleInMillimetres(fudgeDepth, self.Width, col3, row5)
		
		### COLUMN 4 ###
		#create top right flap		
		self._CreatePathinMillimetres(
			[
				["m", col4, row3], 
				["h", self.Depth],
				["l", -self.FlapOffset, -self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
		
		#create box right side print panel
		self._CreateRectangleInMillimetres(self.Height, self.Depth, col4, row3)
		
		#create bottom right flap		
		self._CreatePathinMillimetres(
			[
				["m", col4, row4], 
				["h", self.Depth],
				["l", -self.FlapOffset, self.FlapHeight],
				["h", -(self.Depth - (2*self.FlapOffset))],
			])
		
		### COLUMN 5 ###
		#create right overlap panel
		self._CreateRectangleInMillimetres(self.Height, fudgeDepth, col5, row3)
		
class TuckboxCreateEffect(inkex.Effect):
	def __init__(self):
		inkex.Effect.__init__(self)
		self.OptionParser.add_option('-t', '--type', action = 'store', type = 'string', dest = 'BoxType')
		self.OptionParser.add_option('-n', '--num_flaps', action = 'store', type = 'int', dest = 'NumFlaps')		
		self.OptionParser.add_option('-w', '--deck_width', action = 'store', type = 'float', dest = 'DeckWidth')
		self.OptionParser.add_option('-r', '--deck_height', action = 'store', type = 'float', dest = 'DeckHeight')
		self.OptionParser.add_option('-d', '--deck_depth', action = 'store', type = 'float', dest = 'DeckDepth')
		self.OptionParser.add_option('-a', '--box_allowance', action = 'store', type = 'float', dest = 'DeckAllowance')
									
	def GetPaths(self):
		paths = []
	
	def effect(self):
		dimProvider = EffectDimensionProvider(self)
		
		if self.options.BoxType == "TUCKBOX":
			if self.options.NumFlaps == 2:
				box = DoubleFlappedTuckBox(dimProvider)
			else:		
				box = SingleFlappedTuckBox(dimProvider)
		elif self.options.BoxType == "SLIPCASE":
			box = SlipcaseTuckBox(dimProvider)
		elif self.options.BoxType == "MATCHBOX":
			dimProvider.MaximiseHeight()
			box = Matchbox(dimProvider, self.options.NumFlaps)
		else:
			raise Exception("Box type '{0}' is undefined".format(self.options.BoxType))
	
		box.Create()

if __name__ == '__main__':
	creator = TuckboxCreateEffect()
	
	creator.affect()
