proc srcMovedOrResizedHandler() { codeContractViolation() }

begintemplate ExtracellularSource
    
    public x, y, z, radius, startTime, endTime, colour, species
    public addParamsToPanel, getCentreScreenCoords, moveSphere, resizeSphere
    public isSourceActive
    
    public cachedDistToClosestSegm
    public hasContactWithTheCell, warnIfNoContactWithTheCell
    
    external srcMovedOrResizedHandler
    external math, mwh
    external codeContractViolation
    
    strdef species
    
    
    proc init() {
        x = $1
        y = $2
        z = $3
        radius = $4
        startTime = $5
        endTime = $6
        colour = $7
        species = $s8
        
        cachedDistToClosestSegm = -1
    }
    
    proc addParamsToPanel() {
        strdef hocCommand
        
        hocCommand = "srcMovedOrResizedHandler()"
        
        xpvalue("X coordinate (um)", &x, 1, hocCommand, 0, 1)
        xpvalue("Y coordinate (um)", &y, 1, hocCommand, 0, 1)
        xpvalue("Z coordinate (um)", &z, 1, hocCommand, 0, 1)
        xpvalue("Input radius (um)", &radius, 1, hocCommand, 0, 1)
        xpvalue("Input onset (ms)", &startTime, 1, "", 0, 1)
        xpvalue("Input end (ms)", &endTime, 1, "", 0, 1)
    }
    
    proc getCentreScreenCoords() { local viewDirIdx
        viewDirIdx = $1
        
        if (viewDirIdx == 0) {          // XY plane
            $&2 = x
            $&3 = y
        } else if (viewDirIdx == 1) {   // ZY plane
            $&2 = z
            $&3 = y
        } else if (viewDirIdx == 2) {   // XZ plane
            $&2 = x
            $&3 = z
        } else {
            codeContractViolation()
        }
    }
    
    proc moveSphere() { local viewDirIdx, x_screen, y_screen
        viewDirIdx = $1
        x_screen = $2
        y_screen = $3
        
        if (viewDirIdx == 0) {          // XY plane
            x = x_screen
            y = y_screen
        } else if (viewDirIdx == 1) {   // ZY plane
            z = x_screen
            y = y_screen
        } else if (viewDirIdx == 2) {   // XZ plane
            x = x_screen
            z = y_screen
        } else {
            codeContractViolation()
        }
    }
    
    proc resizeSphere() { local viewDirIdx, x_screen, y_screen, xc_screen, yc_screen, distSq
        viewDirIdx = $1
        x_screen = $2
        y_screen = $3
        
        getCentreScreenCoords(viewDirIdx, &xc_screen, &yc_screen)
        distSq = math.sumSq(x_screen - xc_screen, y_screen - yc_screen)
        radius = sqrt(distSq)
    }
    
    func hasContactWithTheCell() {
        return (cachedDistToClosestSegm < radius)
    }
    
    func warnIfNoContactWithTheCell() { local isDefaultSrc, isWarn
        strdef defaultOrEmpty, line1, line2
        
        isDefaultSrc = $1
        
        isWarn = !hasContactWithTheCell()
        if (isWarn) {
            if (isDefaultSrc) {
                defaultOrEmpty = " default"
            } else {
                defaultOrEmpty = ""
            }
            sprint(line1, "The%s extracellular source of %s has no contact with the astrocyte.", defaultOrEmpty, species)
            line2 = "Please move the sphere closer to the cell or make it bigger."
            mwh.showWarningBox(line1, line2)
        }
        
        return isWarn
    }
    
    // !! ideally, loop by all segments here (but need to cache all segment centre coords in advance)
    func isSourceActive() { local ptIdx, distSq
        
        if (t < startTime || t > endTime) {
            return 0
        }
         
        ptIdx = int(n3d() / 2)
        distSq = math.sumSq(x - x3d(ptIdx), y - y3d(ptIdx), z - z3d(ptIdx))
        return (distSq < radius ^ 2)
    }
    
endtemplate ExtracellularSource