stream = Rx.Observable

apply = (o, func) -> func o

animation = stream.create (observer) ->
  d3.timer -> observer.onNext()
.timestamp()

resize = stream.fromEvent window, 'resize'
  .startWith {}
  .map ->
    width: window.innerWidth
    height: window.innerHeight

updateCameraSize = resize.map (s) ->
  (c) ->
    c.aspect = s.width / s.height
    c.updateProjectionMatrix()
    return c

camera = stream.just new THREE.PerspectiveCamera()
  .concat stream.just (c) ->
    c.position.z = 5
    return c
  .concat updateCameraSize
  .scan apply

renderer = stream.just new THREE.WebGLRenderer()
  .concat stream.just (r) -> r.setClearColor 'white'; r
  .concat resize.map (s) -> (r) -> r.setSize s.width, s.height; r
  .scan apply

renderer.first()
  .subscribe (renderer) ->
    document.body.appendChild renderer.domElement

geometry = stream.just new THREE.SphereGeometry 1, 1, 1

material = stream.just new THREE.MeshBasicMaterial
  color: 'grey', wireframe: true

newCube = stream.combineLatest geometry, material
  .map (arr) ->
    [geom, mat] = arr
    mesh = new THREE.Mesh(geom, mat)
    mesh.name = 'cube'
    return mesh
  .shareReplay() # Re-send values to second subscriber

cubeUpdate = animation.map (time) ->
  (cube) ->
    cube.rotation.x += 0.01
    cube.rotation.y += 0.01
    return cube

addNewCube = newCube.map (c) ->
  (s) ->
    s.add c
    return s

scene = stream.just new THREE.Scene()
  .concat addNewCube
  .scan apply

cube = newCube
  .concat cubeUpdate
  .scan apply

stream.combineLatest renderer, scene, camera, cube
  .subscribe (arr) ->
    [renderer, scene, camera, cube] = arr
    renderer.render scene, camera