# encoding: UTF-8

# KLayout Layout Viewer
# Copyright (C) 2006-2018 Matthias Koefferlein
#
# 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

if !$:.member?(File::dirname($0))
  $:.push(File::dirname($0))
end

load("test_prologue.rb")


class Tl_TestClass < TestBase

  # Expression basics
  def test_1_Expression

    expr = RBA::Expression::new
    res = expr.eval
    assert_equal(res.class.to_s, "NilClass")
    assert_equal(res.to_s, "")

    expr = RBA::Expression.eval("1+2")
    assert_equal(expr.class.to_s, "Float")
    assert_equal(expr.to_s, "3.0")

    expr = RBA::Expression::new
    expr.text = "1+2"
    res = expr.eval
    assert_equal(res.class.to_s, "Float")
    assert_equal(res.to_s, "3.0")

    expr = RBA::Expression::new
    expr.var("a", 5)
    expr.text = "a+to_i(2)"
    res = expr.eval
    assert_equal(res.class.to_s == "Fixnum" || res.class.to_s == "Integer", true)
    assert_equal(res.to_s, "7")
    expr.var("a", 7)
    res = expr.eval
    assert_equal(res.to_s, "9")

    RBA::Expression::global_var("xxx", 17.5)
    expr = RBA::Expression::new("xxx+1")
    res = expr.eval
    assert_equal(res.class.to_s, "Float")
    assert_equal(res.to_s, "18.5")

    expr = RBA::Expression::new("a+b*2", { "a" => 18, "b" => 2.5 })
    res = expr.eval
    assert_equal(res.class.to_s, "Float")
    assert_equal(res.to_s, "23.0")

    expr = RBA::Expression::new("[a[1],a[2],a[0],a[4],a[3]]", { "a" => [ 17, "a", nil, [ 2, 7 ], { 8 => "x", "u" => 42 } ] })
    res = expr.eval
    assert_equal(res.class.to_s, "Array")
    assert_equal(res.inspect, "[\"a\", nil, 17, {8=>\"x\", \"u\"=>42}, [2, 7]]")

    expr = RBA::Expression::new("a[1]", { "a" => [ 17, "a", nil, [ 2, 7 ], { 8 => "x", "u" => 42 } ] })
    res = expr.eval
    assert_equal(res.class.to_s, "String")
    assert_equal(res.to_s, "a")

    expr = RBA::Expression::new("a[4]", { "a" => [ 17, "a", nil, [ 2, 7 ], { 8 => "x", "u" => 42 } ] })
    res = expr.eval
    assert_equal(res.class.to_s, "Hash")
    assert_equal(res.inspect, "{8=>\"x\", \"u\"=>42}")

  end

  # Advanced expressions 
  def test_2_Expression

    box1 = RBA::Box::new(0, 100, 200, 300)
    box2 = RBA::Box::new(50, 150, 250, 350)
    expr = RBA::Expression::new("a", { "a" => box1, "b" => box2 })
    res = expr.eval

    assert_equal(res.to_s, "(0,100;200,300)")

    # boxes are non-managed objects -> passing the object through the expression does not persist their ID
    assert_not_equal(res.object_id, box1.object_id)
    assert_not_equal(res.object_id, box2.object_id)

    # -------------------------------------------------

    box1 = RBA::Box::new(0, 100, 200, 300)
    box2 = RBA::Box::new(50, 150, 250, 350)
    expr = RBA::Expression::new("a&b", { "a" => box1, "b" => box2 })
    res = expr.eval

    assert_equal(res.to_s, "(50,150;200,300)")

    # computed objects are entirely new ones
    assert_not_equal(res.object_id, box1.object_id)
    assert_not_equal(res.object_id, box2.object_id)

    # -------------------------------------------------

    box1 = RBA::Box::new(0, 100, 200, 300)
    box2 = RBA::Box::new(50, 150, 250, 350)
    expr = RBA::Expression::new("x=a&b; y=x; z=y; [x,y,z]", { "a" => box1, "b" => box2, "x" => nil, "y" => nil, "z" => nil })
    res = expr.eval

    assert_equal(res.inspect, "[(50,150;200,300), (50,150;200,300), (50,150;200,300)]")

    # all objects are individual copies
    assert_not_equal(res[0].object_id, box1.object_id)
    assert_not_equal(res[0].object_id, box2.object_id)
    assert_not_equal(res[1].object_id, res[0].object_id)
    assert_not_equal(res[2].object_id, res[0].object_id)

    # -------------------------------------------------

    box1 = RBA::Box::new(0, 100, 200, 300)
    box2 = RBA::Box::new(50, 150, 250, 350)
    expr = RBA::Expression::new("var x=a&b; var y=x; var z=y; [x,y,z]", { "a" => box1, "b" => box2 })
    res = expr.eval

    assert_equal(res.inspect, "[(50,150;200,300), (50,150;200,300), (50,150;200,300)]")

    # all objects are individual copies
    assert_not_equal(res[0].object_id, box1.object_id)
    assert_not_equal(res[0].object_id, box2.object_id)
    assert_not_equal(res[1].object_id, res[0].object_id)
    assert_not_equal(res[2].object_id, res[0].object_id)

    # destruction of the expression's object space does not matter since we have copies
    expr._destroy
    assert_equal(res.inspect, "[(50,150;200,300), (50,150;200,300), (50,150;200,300)]")

    # -------------------------------------------------

    region1 = RBA::Region::new
    region1 |= RBA::Box::new(0, 100, 200, 300)
    region2 = RBA::Region::new
    region2 |= RBA::Box::new(50, 150, 250, 350)
    expr = RBA::Expression::new("a", { "a" => region1, "b" => region2 })
    res = expr.eval

    # regions are managed objects -> passing the object through the expression persists it's object ID
    assert_equal(res.object_id, region1.object_id)
    assert_not_equal(res.object_id, region2.object_id)

    # -------------------------------------------------

    region1 = RBA::Region::new
    region1 |= RBA::Box::new(0, 100, 200, 300)
    region2 = RBA::Region::new
    region2 |= RBA::Box::new(50, 150, 250, 350)
    expr = RBA::Expression::new("a&b", { "a" => region1, "b" => region2, "x" => nil, "y" => nil, "z" => nil })
    res = expr.eval

    assert_equal(res.to_s, "(50,150;50,300;200,300;200,150)")

    # The returned object (as a new one) is an entirely fresh one
    assert_not_equal(res.object_id, region1.object_id)
    assert_not_equal(res.object_id, region2.object_id)

    # -------------------------------------------------

    region1 = RBA::Region::new
    region1 |= RBA::Box::new(0, 100, 200, 300)
    region2 = RBA::Region::new
    region2 |= RBA::Box::new(50, 150, 250, 350)
    expr = RBA::Expression::new("x=a&b; y=x; z=y; [x,y,z]", { "a" => region1, "b" => region2, "x" => nil, "y" => nil, "z" => nil })
    res = expr.eval

    assert_equal(res.inspect, "[(50,150;50,300;200,300;200,150), (50,150;50,300;200,300;200,150), (50,150;50,300;200,300;200,150)]")

    # regions are managed objects -> passing the object through the expression persists it's object ID
    assert_not_equal(res[0].object_id, region1.object_id)
    assert_not_equal(res[0].object_id, region2.object_id)
    assert_equal(res[1].object_id, res[0].object_id)
    assert_equal(res[2].object_id, res[0].object_id)

    # -------------------------------------------------

    region1 = RBA::Region::new
    region1 |= RBA::Box::new(0, 100, 200, 300)
    region2 = RBA::Region::new
    region2 |= RBA::Box::new(50, 150, 250, 350)
    expr = RBA::Expression::new("var x=a&b; var y=x; var z=y; [x,y,z]", { "a" => region1, "b" => region2 })
    res = expr.eval

    assert_equal(res.inspect, "[(50,150;50,300;200,300;200,150), (50,150;50,300;200,300;200,150), (50,150;50,300;200,300;200,150)]")

    # regions are managed objects -> passing the object through the expression persists it's object ID
    assert_not_equal(res[0].object_id, region1.object_id)
    assert_not_equal(res[0].object_id, region2.object_id)
    assert_equal(res[1].object_id, res[0].object_id)
    assert_equal(res[2].object_id, res[0].object_id)

    # the result objects live in the expression object space and are destroyed with the expression
    expr._destroy

    assert_equal(res.size, 3)
    assert_equal(res[0].destroyed?, true)
    assert_equal(res[1].destroyed?, true)
    assert_equal(res[2].destroyed?, true)

  end

end

load("test_epilogue.rb")
