{-
  Copyright 2016 The CodeWorld Authors. All rights reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-}

module CodeWorld.Picture where

import CodeWorld.Color
import Data.Monoid ((<>))
import Data.Text (Text, pack)

type Point = (Double, Double)
type Vector = (Double, Double)

vectorSum :: Vector -> Vector -> Vector
vectorSum (x1,y1) (x2,y2) = (x1 + x2, y1 + y2)

vectorDifference :: Vector -> Vector -> Vector
vectorDifference (x1,y1) (x2,y2) = (x1 - x2, y1 - y2)

scaledVector :: Double -> Vector -> Vector
scaledVector k (x,y) = (k*x, k*y)

{-| Angle is in radians -}
rotatedVector :: Double -> Vector -> Vector
rotatedVector angle (x,y) = (x * cos angle - y * sin angle,
                             x * sin angle + y * cos angle)

dotProduct :: Vector -> Vector -> Double
dotProduct (x1, y1) (x2, y2) = x1 * x2 + y1 * y2

data Picture = Polygon [Point] !Bool
             | Path [Point] !Double !Bool !Bool
             | Sector !Double !Double !Double
             | Arc !Double !Double !Double !Double
             | Text !TextStyle !Font !Text
             | Color !Color !Picture
             | Translate !Double !Double !Picture
             | Scale !Double !Double !Picture
             | Rotate !Double !Picture
             | Pictures [Picture]
             | Logo

data TextStyle = Plain | Bold | Italic

data Font = SansSerif | Serif | Monospace | Handwriting | Fancy | NamedFont !Text

-- | A blank picture
blank :: Picture
blank = Pictures []

-- | A thin sequence of line segments, with these points as endpoints
path :: [Point] -> Picture
path ps = Path ps 0 False False

-- | A thick sequence of line segments, with given line width and endpoints
thickPath :: Double -> [Point] -> Picture
thickPath n ps = Path ps n False False

-- | A thin sequence of line segments, with these points as endpoints
line :: [Point] -> Picture
line ps = Path ps 0 False False
{-# WARNING line "Please use path instead of line" #-}

-- | A thick sequence of line segments, with this line width and endpoints
thickLine :: Double -> [Point] -> Picture
thickLine n ps = Path ps n False False
{-# WARNING thickLine "Please use thickPath instead of thickLine" #-}

-- | A thin polygon with these points as vertices
polygon :: [Point] -> Picture
polygon ps = Path ps 0 True False

-- | A thick polygon with this line width and these points as
-- vertices
thickPolygon :: Double -> [Point] -> Picture
thickPolygon n ps = Path ps n True False

-- | A solid polygon with these points as vertices
solidPolygon :: [Point] -> Picture
solidPolygon ps = Polygon ps False

-- | A smooth curve passing through these points.
curve :: [Point] -> Picture
curve ps = Path ps 0 False True

-- | A thick smooth curve with this line width, passing through these points.
thickCurve :: Double -> [Point] -> Picture
thickCurve n ps = Path ps n False True

-- | A smooth closed loop passing through these points.
loop :: [Point] -> Picture
loop ps = Path ps 0 True True

-- | A thick smooth closed loop with this line width, passing through these points.
thickLoop :: Double -> [Point] -> Picture
thickLoop n ps = Path ps n True True

-- | A solid smooth closed loop passing through these points.
solidLoop :: [Point] -> Picture
solidLoop ps = Polygon ps True

-- | A thin rectangle, with this width and height
rectangle :: Double -> Double -> Picture
rectangle w h = polygon [
    (-w/2, -h/2), (w/2, -h/2), (w/2, h/2), (-w/2, h/2)
    ]

-- | A solid rectangle, with this width and height
solidRectangle :: Double -> Double -> Picture
solidRectangle w h = solidPolygon [
    (-w/2, -h/2), (w/2, -h/2), (w/2, h/2), (-w/2, h/2)
    ]

-- | A thick rectangle, with this line width, and width and height
thickRectangle :: Double -> Double -> Double -> Picture
thickRectangle lw w h = thickPolygon lw [
    (-w/2, -h/2), (w/2, -h/2), (w/2, h/2), (-w/2, h/2)
    ]

-- | A thin circle, with this radius
circle :: Double -> Picture
circle = arc 0 360

-- | A thick circle, with this line width and radius
thickCircle :: Double -> Double -> Picture
thickCircle w = thickArc w 0 360

-- | A thin arc, starting and ending at these angles, with this radius
--
-- Angles are in radians.
arc :: Double -> Double -> Double -> Picture
arc b e r = Arc b e r 0

-- | A thick arc with this line width, starting and ending at these angles,
-- with this radius.
--
-- Angles are in radians.
thickArc :: Double -> Double -> Double -> Double -> Picture
thickArc w b e r = Arc b e r w

-- | A solid circle, with this radius
solidCircle :: Double -> Picture
solidCircle = sector 0 360

-- | A solid sector of a circle (i.e., a pie slice) starting and ending at these
-- angles, with this radius
--
-- Angles are in radians.
sector :: Double -> Double -> Double -> Picture
sector = Sector

-- | A piece of text
text :: Text -> Picture
text = Text Plain Serif

styledText :: TextStyle -> Font -> Text -> Picture
styledText = Text

-- | A picture drawn entirely in this color.
colored :: Color -> Picture -> Picture
colored = Color

-- | A picture drawn entirely in this color.
coloured :: Color -> Picture -> Picture
coloured = Color

-- | A picture drawn translated in these directions.
translated :: Double -> Double -> Picture -> Picture
translated = Translate

-- | A picture scaled by these factors.
scaled :: Double -> Double -> Picture -> Picture
scaled = Scale

-- | A picture scaled by these factors.
dilated :: Double -> Double -> Picture -> Picture
dilated = Scale

-- | A picture rotated by this angle.
--
-- Angles are in radians.
rotated :: Double -> Picture -> Picture
rotated = Rotate

-- A picture made by drawing these pictures, ordered from top to bottom.
pictures :: [Picture] -> Picture
pictures = Pictures

-- Binary composition of pictures.
(&) :: Picture -> Picture -> Picture
infixr 0 &
a & Pictures bs = Pictures (a:bs)
a & b           = Pictures [a, b]
{-# WARNING (&) "Please use <> from Data.Monoid instead of &" #-}

instance Monoid Picture where
  mempty = blank
  mappend = (&)
  mconcat = pictures

-- | A coordinate plane.  Adding this to your pictures can help you measure distances
-- more accurately.
--
-- Example:
--
--    main = pictureOf(myPicture & coordinatePlane)
--    myPicture = ...
coordinatePlane :: Picture
coordinatePlane = axes & numbers & guidelines
  where xline y     = line [(-10, y), (10, y)]
        xaxis       = colored (RGBA 0 0 0 0.75) (xline 0)
        axes        = xaxis <> rotated (pi/2) xaxis
        xguidelines = pictures
            [colored (RGBA 0 0 0 0.25) (xline k) | k <- [-10, -9 .. 10]]
        guidelines  = xguidelines <> rotated (pi/2) xguidelines
        numbers = xnumbers <> ynumbers
        xnumbers = pictures
            [ translated (fromIntegral k) 0.3 (scaled 0.5 0.5 (text (pack (show k))))
              | k <- [-9, -8 .. 9], k /= 0 ]
        ynumbers = pictures
            [ translated 0.3 (fromIntegral k) (scaled 0.5 0.5 (text (pack (show k))))
              | k <- [-9, -8 .. 9], k /= 0 ]

-- | The CodeWorld logo.
codeWorldLogo :: Picture
codeWorldLogo = Logo