package shape

import (
	

	
	
	
)

const (
	SQUARE_TYPE        = "Square"
	REAL_SQUARE_TYPE   = "RealSquare"
	PARALLELOGRAM_TYPE = "Parallelogram"
	DOCUMENT_TYPE      = "Document"
	CYLINDER_TYPE      = "Cylinder"
	QUEUE_TYPE         = "Queue"
	PAGE_TYPE          = "Page"
	PACKAGE_TYPE       = "Package"
	STEP_TYPE          = "Step"
	CALLOUT_TYPE       = "Callout"
	STORED_DATA_TYPE   = "StoredData"
	PERSON_TYPE        = "Person"
	DIAMOND_TYPE       = "Diamond"
	OVAL_TYPE          = "Oval"
	CIRCLE_TYPE        = "Circle"
	HEXAGON_TYPE       = "Hexagon"
	CLOUD_TYPE         = "Cloud"

	TABLE_TYPE = "Table"
	CLASS_TYPE = "Class"
	TEXT_TYPE  = "Text"
	CODE_TYPE  = "Code"
	IMAGE_TYPE = "Image"

	defaultPadding = 40.
)

type Shape interface {
	Is(shape string) bool
	GetType() string

	AspectRatio1() bool
	IsRectangular() bool

	GetBox() *geo.Box
	GetInnerBox() *geo.Box
	// cloud shape has different innerBoxes depending on content's aspect ratio
	GetInnerBoxForContent(width, height float64) *geo.Box
	SetInnerBoxAspectRatio(aspectRatio float64)

	// placing a rectangle of the given size and padding inside the shape, return the position relative to the shape's TopLeft
	GetInsidePlacement(width, height, paddingX, paddingY float64) geo.Point

	GetDimensionsToFit(width, height, paddingX, paddingY float64) (float64, float64)
	GetDefaultPadding() (paddingX, paddingY float64)

	// Perimeter returns a slice of geo.Intersectables that together constitute the shape border
	Perimeter() []geo.Intersectable

	GetSVGPathData() []string
}

type baseShape struct {
	Type      string
	Box       *geo.Box
	FullShape *Shape
}

func ( baseShape) ( string) bool {
	return .Type == 
}

func ( baseShape) () string {
	return .Type
}

func ( baseShape) () bool {
	return false
}

func ( baseShape) () bool {
	return false
}

func ( baseShape) () *geo.Box {
	return .Box
}

func ( baseShape) () *geo.Box {
	return .Box
}

// only cloud shape needs this right now
func ( baseShape) (,  float64) *geo.Box {
	return nil
}

func ( baseShape) ( float64) {
	// only used for cloud
}

func ( baseShape) (, , ,  float64) geo.Point {
	 := (*.FullShape).GetInnerBox().TopLeft
	return *geo.NewPoint(.X+/2, .Y+/2)
}

// return the minimum shape dimensions needed to fit content (width x height)
// in the shape's innerBox with padding
func ( baseShape) (, , ,  float64) (float64, float64) {
	return math.Ceil( + ), math.Ceil( + )
}

func ( baseShape) () (,  float64) {
	return defaultPadding, defaultPadding
}

func ( baseShape) () []geo.Intersectable {
	return nil
}

func ( baseShape) () []string {
	return nil
}

func ( string,  *geo.Box) Shape {
	switch  {
	case CALLOUT_TYPE:
		return NewCallout()
	case CIRCLE_TYPE:
		return NewCircle()
	case CLASS_TYPE:
		return NewClass()
	case CLOUD_TYPE:
		return NewCloud()
	case CODE_TYPE:
		return NewCode()
	case CYLINDER_TYPE:
		return NewCylinder()
	case DIAMOND_TYPE:
		return NewDiamond()
	case DOCUMENT_TYPE:
		return NewDocument()
	case HEXAGON_TYPE:
		return NewHexagon()
	case IMAGE_TYPE:
		return NewImage()
	case OVAL_TYPE:
		return NewOval()
	case PACKAGE_TYPE:
		return NewPackage()
	case PAGE_TYPE:
		return NewPage()
	case PARALLELOGRAM_TYPE:
		return NewParallelogram()
	case PERSON_TYPE:
		return NewPerson()
	case QUEUE_TYPE:
		return NewQueue()
	case REAL_SQUARE_TYPE:
		return NewRealSquare()
	case STEP_TYPE:
		return NewStep()
	case STORED_DATA_TYPE:
		return NewStoredData()
	case SQUARE_TYPE:
		return NewSquare()
	case TABLE_TYPE:
		return NewTable()
	case TEXT_TYPE:
		return NewText()

	default:
		 := shapeSquare{
			baseShape: &baseShape{
				Type: ,
				Box:  ,
			},
		}
		.FullShape = go2.Pointer(Shape())
		return 
	}
}

// TraceToShapeBorder takes the point on the rectangular border
// r here is the point on rectangular border
// p is the prev point (used to calculate slope)
// s is the point on the actual shape border that'll be returned
//
// .      p
// .      │
// .      │
// .      ▼
// . ┌────r─────────────────────────┐
// . │                              │
// . │    │                         │
// . │    │      xxxxxxxx           │
// . │    ▼  xxxxx       xxxx       │
// . │    sxxx               xx     │
// . │   x                    xx    │
// . │  xx                     xx   │
// . │  x                      xx   │
// . │  xx                   xxx    │
// . │   xxxx             xxxx      │
// . └──────xxxxxxxxxxxxxx──────────┘
func ( Shape, ,  *geo.Point) *geo.Point {
	if .Is("") || .IsRectangular() {
		return 
	}

	// We want to extend the line all the way through to the other end of the shape to get the intersections
	 := .GetBox().Width
	if .X == .X {
		 = .GetBox().Height
	}
	 := .VectorTo()
	 = .AddLength()
	 := geo.Segment{Start: , End: .AddVector()}

	 := math.Inf(1)
	 := 

	for ,  := range .Perimeter() {
		for ,  := range .Intersections() {
			 := geo.EuclideanDistance(.X, .Y, .X, .Y)
			if  <  {
				 = 
				 = 
			}
		}
	}

	.TruncateFloat32()
	return geo.NewPoint(math.Round(.X), math.Round(.Y))
}

func boxPath( *geo.Box) *svg.SvgPathContext {
	 := svg.NewSVGPathContext(.TopLeft, 1, 1)
	.StartAt(.Absolute(0, 0))
	.L(false, .Width, 0)
	.L(false, .Width, .Height)
	.L(false, 0, .Height)
	.Z()
	return 
}

func (, ,  float64) (float64, float64) {
	if  > * {
		 = math.Round( / )
	} else if  > * {
		 = math.Round( / )
	}
	return , 
}