if type(window) ~= "userdata" then
  window = ofWindow()
end

local clock = ofClock(this, "setup")
local DV = class()
function DV:__init(x, y)
  self.x = x
  self.y = y
  self.bBeingDragged = false
  self.bOver = false
  self.radius = 4
end
local nCurveVertexes = 0
local curveVertices = {}

function M.new()
  ofWindow.addListener("setup", this)
  ofWindow.addListener("draw", this)
  ofWindow.addListener("mouseMoved", this)
  ofWindow.addListener("mouseDragged", this)
  ofWindow.addListener("mousePressed", this)
  ofWindow.addListener("mouseReleased", this)
  window:setPosition(30, 100)
  window:setSize(1024, 768)
  if ofWindow.exists then
    clock:delay(0)
  else
    window:create()
  end
end

function M.free()
  window:destroy()
  ofWindow.removeListener("setup", this)
  ofWindow.removeListener("draw", this)
  ofWindow.removeListener("mouseMoved", this)
  ofWindow.removeListener("mouseDragged", this)
  ofWindow.removeListener("mousePressed", this)
  ofWindow.removeListener("mouseReleased", this)
end

function M.setup()
  ofSetWindowTitle("polygon example")
  ofBackground(255, 255, 255, 255)
  nCurveVertexes = 7
  curveVertices = {DV(326, 209), DV(306, 279), DV(265, 331), DV(304, 383), DV(374, 383), DV(418, 209), DV(345, 279)}
end

function M.draw()
  ofFill()

--[[---------------------------------------------------------------
draw a star
use poly winding odd, the default rule
--]]---------------------------------------------------------------
  ofSetHexColor(0xe0be21)
  ofSetPolyMode(OF_POLY_WINDING_ODD)
  ofBeginShape()
  ofVertex(200, 135)
  ofVertex(15, 135)
  ofVertex(165, 25)
  ofVertex(105, 200)
  ofVertex(50, 25)
  ofEndShape()

--[[---------------------------------------------------------------
draw a star
use poly winding nonzero
--]]---------------------------------------------------------------
  ofSetHexColor(0xb5de10)
  ofSetPolyMode(OF_POLY_WINDING_NONZERO)
  ofBeginShape()
  ofVertex(400, 135)
  ofVertex(215, 135)
  ofVertex(365, 25)
  ofVertex(305, 200)
  ofVertex(250, 25)
  ofEndShape()

--[[---------------------------------------------------------------
draw a star dynamically
use the mouse position as a pct
to calc nPoints and internal point radius
--]]---------------------------------------------------------------
  local xPct = ofGetMouseX() / ofGetWidth()
  local yPct = ofGetMouseY() / ofGetHeight()
  local nTips = 5 + xPct * 60
  local nStarPts = math.min(nTips * 2, 100)
  local angleChangePerPt = OF_TWO_PI / nStarPts
  local innerRadius = 0 + yPct * 80
  local outerRadius = 80
  local origx = 525
  local origy = 100
  local angle = 0
  ofSetHexColor(0xa16bca)
  ofBeginShape()
  for i = 0, nStarPts do
    if i % 2 == 0 then
      local x = origx + innerRadius * math.cos(angle)
      local y = origy + innerRadius * math.sin(angle)
      ofVertex(x, y)
    else
      local x = origx + outerRadius * math.cos(angle)
      local y = origy + outerRadius * math.sin(angle)
      ofVertex(x, y)
    end
    angle = angle + angleChangePerPt
  end
  ofEndShape()

--[[---------------------------------------------------------------
poylgon of random points
lots of self intersection, 500 pts is a good stress test
--]]---------------------------------------------------------------
  ofSetHexColor(0x0cb0b6)
  ofSetPolyMode(OF_POLY_WINDING_ODD)
  ofBeginShape()
  for i = 1, 10 do
    ofVertex(ofRandom(650, 850), ofRandom(20, 200))
  end
  ofEndShape()

--[[---------------------------------------------------------------
use sin cos and time to make some spirally shape
--]]---------------------------------------------------------------
  ofPushMatrix()
  ofTranslate(100, 300, 0)
  ofSetHexColor(0xff2220)
  ofFill()
  ofSetPolyMode(OF_POLY_WINDING_ODD)
  ofBeginShape()
  local angleStep = OF_TWO_PI / (100 + math.sin(ofGetElapsedTimef() / 5) * 60)
  local radiusAdder = 0.5
  local radius = 0
  for i = 1, 200 do
    local anglef = (i) * angleStep
    local x = radius * math.cos(anglef)
    local y = radius * math.sin(anglef)
    ofVertex(x, y)
    radius = radius + radiusAdder
  end
  ofEndShape(OF_CLOSE)
  ofPopMatrix()

--[[---------------------------------------------------------------
ofCurveVertex
because it uses catmul rom splines, we need to repeat the first and last
items so the curve actually goes through those points
--]]---------------------------------------------------------------
  ofSetHexColor(0x2bdbe6)
  ofBeginShape()
  for i = 1, nCurveVertexes do
    if i == 1 then
      ofCurveVertex(curveVertices[1].x, curveVertices[1].y)
      ofCurveVertex(curveVertices[1].x, curveVertices[1].y)
    elseif i == nCurveVertexes then
      ofCurveVertex(curveVertices[i].x, curveVertices[i].y)
      ofCurveVertex(curveVertices[1].x, curveVertices[1].y)
      ofCurveVertex(curveVertices[1].x, curveVertices[1].y)
    else
      ofCurveVertex(curveVertices[i].x, curveVertices[i].y)
    end
  end
  ofEndShape()
  ofEnableAlphaBlending()
  ofNoFill()
  ofSetColor(0, 0, 0, 40)
  ofBeginShape()
  for i = 1, nCurveVertexes do
    ofVertex(curveVertices[i].x, curveVertices[i].y)
  end
  ofEndShape(true)
  ofSetColor(0, 0, 0, 80)
  for i = 1, nCurveVertexes do
    if curveVertices[i].bOver == true then
      ofFill()
    else
      ofNoFill()
    end
    ofDrawCircle(curveVertices[i].x, curveVertices[i].y, 4)
  end
  ofDisableAlphaBlending()

--[[---------------------------------------------------------------
ofBezierVertex
with ofBezierVertex we can draw a curve from the current vertex
through the the next three vertexes we pass in.
(two control points and the final bezier point)
--]]---------------------------------------------------------------
  local x0 = 500
  local y0 = 300
  local x1 = 550 + 50 * math.cos(ofGetElapsedTimef() * 1.0)
  local y1 = 300 + 100 * math.sin(ofGetElapsedTimef() / 3.5)
  local x2 = 600 + 30 * math.cos(ofGetElapsedTimef() * 2.0)
  local y2 = 300 + 100 * math.sin(ofGetElapsedTimef())
  local x3 = 650
  local y3 = 300
  ofFill()
  ofSetHexColor(0xFF9933)
  ofBeginShape()
  ofVertex(x0, y0)
  ofBezierVertex(x1, y1, x2, y2, x3, y3)
  ofEndShape()
  ofEnableAlphaBlending()
  ofFill()
  ofSetColor(0, 0, 0, 40)
  ofDrawCircle(x0, y0, 4)
  ofDrawCircle(x1, y1, 4)
  ofDrawCircle(x2, y2, 4)
  ofDrawCircle(x3, y3, 4)
  ofDisableAlphaBlending()

--[[---------------------------------------------------------------
holes / ofNextContour
with ofNextContour we can create multi-contour shapes
this allows us to draw holes, for example...
--]]---------------------------------------------------------------
  ofFill()
  ofSetHexColor(0xd3ffd3)
  ofDrawRectangle(80, 480, 140, 70)
  ofSetHexColor(0xff00ff)
  ofBeginShape()
  ofVertex(100, 500)
  ofVertex(180, 550)
  ofVertex(100, 600)
  ofNextContour(true)
  ofVertex(120, 520)
  ofVertex(160, 550)
  ofVertex(120, 580)
  ofEndShape(true)

--[[---------------------------------------------------------------
CSG / ofNextContour
with different winding rules, you can even use ofNextContour to
perform constructive solid geometry
--]]---------------------------------------------------------------
  ofNoFill()
  ofPushMatrix()
  ofSetPolyMode(OF_POLY_WINDING_ODD)
  ofBeginShape()
  ofVertex(300, 500)
  ofVertex(380, 550)
  ofVertex(300, 600)
  ofNextContour(true)
  for i = 1, 20 do
    local anglef = (i / 19.0) * OF_TWO_PI
    local x = 340 + 30 * math.cos(anglef)
    local y = 550 + 30 * math.sin(anglef)
    ofVertex(x, y)
    radius = radius + radiusAdder
  end
  ofEndShape(true)
  ofTranslate(100, 0, 0)
  ofSetPolyMode(OF_POLY_WINDING_NONZERO)
  ofBeginShape()
  ofVertex(300, 500)
  ofVertex(380, 550)
  ofVertex(300, 600)
  ofNextContour(true)
  for i = 1, 20 do
    local anglef = (i / 19.0) * OF_TWO_PI
    local x = 340 + 30 * math.cos(anglef)
    local y = 550 + 30 * math.sin(anglef)
    ofVertex(x, y)
    radius = radius + radiusAdder
  end
  ofEndShape(true)
  ofTranslate(100, 0, 0)
  ofSetPolyMode(OF_POLY_WINDING_ABS_GEQ_TWO)
  ofBeginShape()
  ofVertex(300, 500)
  ofVertex(380, 550)
  ofVertex(300, 600)
  ofNextContour(true)
  for i = 1, 20 do
    local anglef = (i / 19.0) * OF_TWO_PI
    local x = 340 + 30 * math.cos(anglef)
    local y = 550 + 30 * math.sin(anglef)
    ofVertex(x, y)
    radius = radius + radiusAdder
  end
  ofEndShape(true)
  ofPopMatrix()
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(a) star"..string.char(10).."winding rule odd", 20, 210)
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(b) star"..string.char(10).."winding rule nonzero", 220, 210)
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(c) dynamically"..string.char(10).."created shape", 420, 210)
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(d) random points"..string.char(10).."poly", 670, 210)
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(e) fun with sin/cos", 20, 410)
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(f) ofCurveVertex"..string.char(10).."uses catmull rom"..string.char(10).."to make curved shapes", 220, 410)
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(g) ofBezierVertex"..string.char(10).."uses bezier to draw curves", 460, 410)
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(h) ofNextContour"..string.char(10).."allows for holes", 20, 610)
  ofSetHexColor(0x000000)
  ofDrawBitmapString("(i) ofNextContour"..string.char(10).."can even be used for CSG operations"..string.char(10).."such as union and intersection", 260, 620)
end

function M.mouseMoved(e)
  for i = 1, nCurveVertexes do
    local diffx = e.x - curveVertices[i].x
    local diffy = e.y - curveVertices[i].y
    local dist = math.sqrt(diffx * diffx + diffy * diffy)
    if dist < curveVertices[i].radius then
      curveVertices[i].bOver = true
    else
      curveVertices[i].bOver = false
    end
  end
end

function M.mouseDragged(e)
  for i = 1, nCurveVertexes do
    if curveVertices[i].bBeingDragged == true then
      curveVertices[i].x = e.x
      curveVertices[i].y = e.y
    end
  end
end

function M.mousePressed(e)
  for i = 1, nCurveVertexes do
    local diffx = e.x - curveVertices[i].x
    local diffy = e.y - curveVertices[i].y
    local dist = math.sqrt(diffx * diffx + diffy * diffy)
    if dist < curveVertices[i].radius then
      curveVertices[i].bBeingDragged = true
    else
      curveVertices[i].bBeingDragged = false
    end
  end
end

function M.mouseReleased(e)
  for i = 1, nCurveVertexes do
    curveVertices[i].bBeingDragged = false
  end
end
